Encryptor::encrypt()   A
last analyzed

Complexity

Conditions 3
Paths 4

Size

Total Lines 16
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 10
c 1
b 0
f 0
nc 4
nop 3
dl 0
loc 16
rs 9.9332
1
<?php
2
3
/*
4
 * This file is part of the mingyoung/dingtalk.
5
 *
6
 * (c) 张铭阳 <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled
9
 * with this source code in the file LICENSE.
10
 */
11
12
namespace EasyDingTalk\Kernel\Encryption;
13
14
use function EasyDingTalk\str_random;
15
16
class Encryptor
17
{
18
    /**
19
     * @var string
20
     */
21
    protected $key;
22
23
    /**
24
     * @var string
25
     */
26
    protected $token;
27
28
    /**
29
     * @var string
30
     */
31
    protected $aesKey;
32
33
    /**
34
     * @var int
35
     */
36
    protected $blockSize = 32;
37
38
    /**
39
     * Encryptor Constructor.
40
     *
41
     * @param string $key
42
     * @param string $token
43
     * @param string $aesKey
44
     */
45
    public function __construct($key, $token, $aesKey)
46
    {
47
        $this->key = $key;
48
        $this->token = $token;
49
        $this->aesKey = base64_decode($aesKey.'=', true);
50
    }
51
52
    /**
53
     * Encrypt the data.
54
     *
55
     * @param string $data
56
     * @param string $nonce
57
     * @param int    $timestamp
58
     *
59
     * @return string
60
     */
61
    public function encrypt($data, $nonce = null, $timestamp = null)
62
    {
63
        $string = str_random().pack('N', strlen($data)).$data.$this->key;
64
65
        $result = base64_encode(
66
            openssl_encrypt($this->pkcs7Pad($string), 'AES-256-CBC', $this->aesKey, OPENSSL_NO_PADDING, substr($this->aesKey, 0, 16))
67
        );
68
69
        !is_null($nonce) || $nonce = uniqid();
70
        !is_null($timestamp) || $timestamp = time();
71
72
        return json_encode([
73
            'msg_signature' => $this->signature($this->token, $nonce, $timestamp, $result),
74
            'timeStamp' => $timestamp,
75
            'nonce' => $nonce,
76
            'encrypt' => $result,
77
        ]);
78
    }
79
80
    /**
81
     * Decrypt the data.
82
     *
83
     * @param string $data
84
     * @param string $signature
85
     * @param string $nonce
86
     * @param int    $timestamp
87
     *
88
     * @return string
89
     */
90
    public function decrypt($data, $signature, $nonce, $timestamp)
91
    {
92
        if ($signature !== $this->signature($this->token, $nonce, $timestamp, $data)) {
93
            throw new \RuntimeException('Invalid Signature.');
94
        }
95
96
        $decrypted = openssl_decrypt(
97
            base64_decode($data, true), 'AES-256-CBC', $this->aesKey, OPENSSL_NO_PADDING, substr($this->aesKey, 0, 16)
98
        );
99
100
        $result = $this->pkcs7Unpad($decrypted);
101
102
        $data = substr($result, 16, strlen($result));
103
104
        $contentLen = unpack('N', substr($data, 0, 4))[1];
105
106
        if (substr($data, $contentLen + 4) !== $this->key) {
107
            throw new \RuntimeException('Invalid CorpId.');
108
        }
109
110
        return substr($data, 4, $contentLen);
111
    }
112
113
    /**
114
     * Get SHA1.
115
     *
116
     * @return string
117
     */
118
    public function signature()
119
    {
120
        $array = func_get_args();
121
        sort($array, SORT_STRING);
122
123
        return sha1(implode($array));
124
    }
125
126
    /**
127
     * PKCS#7 pad.
128
     *
129
     * @param string $text
130
     *
131
     * @return string
132
     */
133
    public function pkcs7Pad(string $text)
134
    {
135
        $padding = $this->blockSize - (strlen($text) % $this->blockSize);
136
        $pattern = chr($padding);
137
138
        return $text.str_repeat($pattern, $padding);
139
    }
140
141
    /**
142
     * PKCS#7 unpad.
143
     *
144
     * @param string $text
145
     *
146
     * @return string
147
     */
148
    public function pkcs7Unpad(string $text)
149
    {
150
        $pad = ord(substr($text, -1));
151
        if ($pad < 1 || $pad > $this->blockSize) {
152
            $pad = 0;
153
        }
154
155
        return substr($text, 0, (strlen($text) - $pad));
156
    }
157
}
158