微信小程序敏感信息解密代码中Mcrypt被PHP7.1废弃的解决方案

一、问题

微信小程序开发的加解密demo代码中,使用了 Mcrypt ,而mcrypt在php7.1已经算是被废弃了。强制使用将会出现如下问题:

1
2
3
4
{
"message":"Function mcrypt_module_open() is deprecated",
"status_code":500
}

二、解决

主要是在 Prpcrypt 这个类里面中使用的代码

  • 之前的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 对密文进行解密
* @param string $aesCipher 需要解密的密文
* @param string $aesIV 解密的初始向量
* @return array 解密得到的明文
*/
public function decrypt($aesCipher, $aesIV)
{
try {

$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');// 此处php < 7.1 支持

mcrypt_generic_init($module, $this->key, $aesIV);

//解密
$decrypted = mdecrypt_generic($module, $aesCipher);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
} catch (Exception $e) {
return array(ErrorCode::$IllegalBuffer, null);
}
}
  • 修改之后的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

/**
* 对密文进行解密
* @param string $aesCipher 需要解密的密文
* @param string $aesIV 解密的初始向量
* @return array 解密得到的明文
*/
public function decrypt($aesCipher, $aesIV)
{
try {
//解密
$decrypted = openssl_decrypt($aesCipher, 'aes-128-cbc', $this->key, OPENSSL_RAW_DATA, $aesIV);
} catch (\Exception $e) {
return [ErrorCode::$IllegalBuffer, null];
}
try {
//去除补位字符
$pkc_encoder = new PKCS7Encoder;
$result = $pkc_encoder->decode($decrypted);
} catch (\Exception $e) {
return [ErrorCode::$IllegalBuffer, null];
}
return [0, $result];
}

三、注意

需要安装openssl扩展!!

四、DEMO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<?php
namespace App\Extend;


class WxDataDecrypt
{
private $appid;
private $sessionKey;

/**
* 构造函数
* @param $sessionKey string 用户在小程序登录后获取的会话密钥
* @param $appId string 小程序的appId
*/
public function __construct($appid, $sessionKey)
{
$this->sessionKey = $sessionKey;
$this->appid = $appid;
}

/**
* 检验数据的真实性,并且获取解密后的明文.
* @param $encryptedData string 加密的用户数据
* @param $iv string 与用户数据一同返回的初始向量
* @param $data string 解密后的原文
*
* @return int 成功0,失败返回对应的错误码
*/
public function decryptData($encryptedData, $iv, &$data)
{
if (strlen($this->sessionKey) != 24) {
return ErrorCode::$IllegalAesKey;
}
$aesKey = base64_decode($this->sessionKey);

if (strlen($iv) != 24) {
return ErrorCode::$IllegalIv;
}
$aesIV = base64_decode($iv);

$aesCipher = base64_decode($encryptedData);

$pc = new Prpcrypt($aesKey);
$result = $pc->decrypt($aesCipher, $aesIV);

if ($result[0] != 0) {
return $result[0];
}

$dataObj = json_decode($result[1]);
if ($dataObj == NULL) {
return ErrorCode::$IllegalBuffer;
}
if ($dataObj->watermark->appid != $this->appid) {
return ErrorCode::$IllegalBuffer;
}
$data = $result[1];
return ErrorCode::$OK;
}

}


/**
* PKCS7Encoder class
*
* 提供基于PKCS7算法的加解密接口.
*/
class PKCS7Encoder
{
public static $block_size = 32;

/**
* 对需要加密的明文进行填充补位
* @param string $text 需要进行填充补位操作的明文
* @return string 补齐明文字符串
*/
function encode($text)
{
$block_size = self::$block_size;
$text_length = strlen($text);
//计算需要填充的位数
$amount_to_pad = $block_size - ($text_length % $block_size);
if ($amount_to_pad == 0)
$amount_to_pad = $block_size;
//获得补位所用的字符
$pad_chr = chr($amount_to_pad);
$tmp = "";
for ($index = 0; $index < $amount_to_pad; $index++)
$tmp .= $pad_chr;
return $text . $tmp;
}


/**
* 对解密后的明文进行补位删除
* @param string $text 解密后的明文
* @return string 删除填充补位后的明文
*/
function decode($text)
{
$pad = ord(substr($text, -1));
if ($pad < 1 || $pad > self::$block_size)
$pad = 0;
return substr($text, 0, (strlen($text) - $pad));
}
}

/**
* Prpcrypt class
*
*
*/
class Prpcrypt
{
public $key;

function __construct($key)
{
$this->key = $key;
}

/**
* 对密文进行解密
* @param string $aesCipher 需要解密的密文
* @param string $aesIV 解密的初始向量
* @return array 解密得到的明文
*/
public function decrypt($aesCipher, $aesIV)
{
try {
//解密
$decrypted = openssl_decrypt($aesCipher, 'aes-128-cbc', $this->key, OPENSSL_RAW_DATA, $aesIV);
} catch (\Exception $e) {
return [ErrorCode::$IllegalBuffer, null];
}
try {
//去除补位字符
$pkc_encoder = new PKCS7Encoder;
$result = $pkc_encoder->decode($decrypted);
} catch (\Exception $e) {
return [ErrorCode::$IllegalBuffer, null];
}
return [0, $result];
}
}


/**
* error code 说明.
* <ul>
* <li>-41001: encodingAesKey 非法</li>
* <li>-41003: aes 解密失败</li>
* <li>-41004: 解密后得到的buffer非法</li>
* <li>-41005: base64加密失败</li>
* <li>-41016: base64解密失败</li>
* </ul>
*/
class ErrorCode
{
public static $OK = 0;
public static $IllegalAesKey = -41001;
public static $IllegalIv = -41002;
public static $IllegalBuffer = -41003;
public static $DecodeBase64Error = -41004;
public static $DecryptAESError = -41005;
public static $ValidateAppidError = -41006;
public static $ErrorMsg = [
0 => 'ok',
-41001 => 'aes 解密失败',
-41002 => '解密后得到的buffer非法',
-41003 => 'base64加密失败',
-41004 => 'base64解密失败'
];
}

// 获取手机号
$tool = new WxDataDecrypt(ENV('WX_APPID'), $arr['_session_key']);
$error = $tool->decryptData($arr['encryptedData'], $arr['iv'], $decryptRet);
if ($error) {
echo ErrorCode::$ErrorMsg[$error] ?? '未知错误';
} else {
$decryptRet = json_decode($decryptRet, true);
$tel_phone = $decryptRet['purePhoneNumber'];
echo "phone: {$tel_phone}";
}
?>

五、简化

上面的代码比较复杂,那就看下面的代码吧, 调用方法完全一致。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?php

class WxDataDecrypt
{
private $appid;
private $sessionKey;

/**
* error code 说明.
* <ul>
* <li>-41001: encodingAesKey 非法</li>
* <li>-41003: aes 解密失败</li>
* <li>-41004: 解密后得到的buffer非法</li>
* <li>-41005: base64加密失败</li>
* <li>-41016: base64解密失败</li>
* </ul>
*/
public static $OK = 0;
public static $IllegalAesKey = -41001;
public static $IllegalIv = -41002;
public static $IllegalBuffer = -41003;
public static $DecodeBase64Error = -41004;
public static $ErrorMsg = [
0 => 'ok',
-41001 => 'aes 解密失败',
-41002 => '解密后得到的buffer非法',
-41003 => 'base64加密失败',
-41004 => 'base64解密失败'
];

/**
* 构造函数
* @param $sessionKey string 用户在小程序登录后获取的会话密钥
* @param $appid string 小程序的appid
*/
public function __construct($appid, $sessionKey)
{
$this->appid = $appid;
$this->sessionKey = $sessionKey;
}

/**
* 检验数据的真实性,并且获取解密后的明文.
* @param $encryptedData string 加密的用户数据
* @param $iv string 与用户数据一同返回的初始向量
* @param $data string 解密后的原文
*
* @return int 成功0,失败返回对应的错误码
*/
public function decryptData($encryptedData, $iv, &$data)
{
if (strlen($this->sessionKey) != 24) {
return self::$IllegalAesKey;
}
$aesKey = base64_decode($this->sessionKey);

if (strlen($iv) != 24) {
return self::$IllegalIv;
}
$aesIV = base64_decode($iv);

$aesCipher = base64_decode($encryptedData);

$result = openssl_decrypt($aesCipher, "AES-128-CBC", $aesKey, OPENSSL_RAW_DATA, $aesIV);

$dataObj = json_decode($result);
if ($dataObj == NULL) {
return self::$IllegalBuffer;
}
if ($dataObj->watermark->appid != $this->appid) {
return self::$IllegalBuffer;
}
$data = $result;
return self::$OK;
}
}
关注作者公众号,获取更多资源!
赏作者一杯咖啡~