php7.1微信公眾平臺解密失敗DecryptAESError = -40007
php7.1釋出後新特性吸引了不少PHPer,大家都在討論新特性帶來的好處與便利。但是從php7.0 升級到 php7.1 廢棄(過時)了一個在過去普遍應用的擴充套件(mcrypt擴充套件)。官方提供了相應的解決提示,卻沒有提供更詳細的解決辦法。於是坑來了….
首頁要確保類的構造方法支援php7版本,PHP 7開始使用和類名相同的方法名作為構造方法會報E_DEPRECATED級別的錯誤,提示在未來版本中會徹底拋棄類同名方法作為建構函式,但程式仍然會正常執行。
Deprecated: Methods with the same name as their class will not be constructor...
這裡涉及兩個檔案的建構函式的修改:
pkcs7Encoder.php,將建構函式改為
/**
* Prpcrypt class
*
* 提供接收和推送給公眾平臺訊息的加解密介面.
*/
class Prpcrypt
{
public $key;
function __construct($k)
{
$this->key = base64_decode($k . "=");
}
以下程式碼省略
topClient.php,建構函式改為
/**
* 1.第三方回覆加密訊息給公眾平臺;
* 2.第三方收到公眾平臺傳送的訊息,驗證訊息的安全性,並對訊息進行解密。
*/
class wXBizMsgCrypt
{
private $token;
private $encodingAesKey;
private $appId;
/**
* 建構函式
* @param $token string 公眾平臺上,開發者設定的token
* @param $encodingAesKey string 公眾平臺上,開發者設定的EncodingAESKey
* @param $appId string 公眾平臺的appId
*/
function __construct($token, $encodingAesKey, $appId)
{
$this->token = $token;
$this->encodingAesKey = $encodingAesKey;
$this->appId = $appId;
}
以下程式碼省略
wxBizMsgCrypt.php,以下程式碼改為
/**
* 1.第三方回覆加密訊息給公眾平臺;
* 2.第三方收到公眾平臺傳送的訊息,驗證訊息的安全性,並對訊息進行解密。
*/
class WXBizMsgCrypt
{
private $token;
private $encodingAesKey;
private $appId;
/**
* 建構函式
* @param $token string 公眾平臺上,開發者設定的token
* @param $encodingAesKey string 公眾平臺上,開發者設定的EncodingAESKey
* @param $appId string 公眾平臺的appId
*/
function __construct($token, $encodingAesKey, $appId)
{
$this->token = $token;
$this->encodingAesKey = $encodingAesKey;
$this->appId = $appId;
}
以下程式碼省略
下面是微信官方提供的訊息加密解密演算法中的核心部分,來自檔案pkcs7Encoder.php
/**
* 對明文進行加密
* @param string $text 需要加密的明文
* @return string 加密後的密文
*/
public function encrypt($text, $appid)
{
try {
//獲得16位隨機字串,填充到明文之前
$random = $this->getRandomStr();
$text = $random . pack("N", strlen($text)) . $text . $appid;
// 網路位元組序
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = substr($this->key, 0, 16);
//使用自定義的填充方式對明文進行補位填充
$pkc_encoder = new PKCS7Encoder;
$text = $pkc_encoder->encode($text);
mcrypt_generic_init($module, $this->key, $iv);
//加密
$encrypted = mcrypt_generic($module, $text);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
//print(base64_encode($encrypted));
//使用BASE64對加密後的字串進行編碼
return array(ErrorCode::$OK, base64_encode($encrypted));
} catch (Exception $e) {
//print $e;
return array(ErrorCode::$EncryptAESError, null);
}
}
/**
* 對密文進行解密
* @param string $encrypted 需要解密的密文
* @return string 解密得到的明文
*/
public function decrypt($encrypted, $appid)
{
try {
//使用BASE64對需要解密的字串進行解碼
$ciphertext_dec = base64_decode($encrypted);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = substr($this->key, 0, 16);
mcrypt_generic_init($module, $this->key, $iv);
//解密
$decrypted = mdecrypt_generic($module, $ciphertext_dec);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
} catch (Exception $e) {
return array(ErrorCode::$DecryptAESError, null);
}
try {
//去除補位字元
$pkc_encoder = new PKCS7Encoder;
$result = $pkc_encoder->decode($decrypted);
//去除16位隨機字串,網路位元組序和AppId
if (strlen($result) < 16)
return "";
$content = substr($result, 16, strlen($result));
$len_list = unpack("N", substr($content, 0, 4));
$xml_len = $len_list[1];
$xml_content = substr($content, 4, $xml_len);
$from_appid = substr($content, $xml_len + 4);
} catch (Exception $e) {
//print $e;
return array(ErrorCode::$IllegalBuffer, null);
}
if ($from_appid != $appid)
return array(ErrorCode::$ValidateAppidError, null);
return array(0, $xml_content);
}
以上程式碼中使用了mcrypt擴充套件
php官方只是輕飄飄的說mcrypt擴充套件被 OpenSSL取代了,卻並未提供相應的轉換辦法。網路提供的演算法大多是mcrypt加密的使用mcrypt解密或者OpenSSL加密的使用OpenSSL解密。並未提供互通替換的方案。
對比研究
作者開始分析這兩種演算法時感覺總是摸不著頭腦,感覺兩種演算法的結果外掛太多,完全不相關聯的樣子。通過一番查閱和反覆測試終於明白這兩種演算法的區別了。
在演算法、data、key、vi 一致的情況下
openssl_encrypt 加密相當於將 mcrypt_encrypt 的加密結果執行一次 base64_encode
而
openssl_decode 解密相當於 先將加密結果執行一次base64_decode 然後再通過mcrypt_encrypt 解密
調整後的程式碼,相容php7
/**
* 對明文進行加密
* @param string $text 需要加密的明文
* @return string 加密後的密文
*/
public function encrypt($text, $appid)
{
try {
if(version_compare(PHP_VERSION, '7','>=')) {
//獲得16位隨機字串,填充到明文之前
$random = $this->getRandomStr();
$text = $random . pack("N", strlen($text)) . $text . $appid;
$iv = substr($this->key, 0, 16);
$pkc_encoder = new PKCS7Encoder;
$text = $pkc_encoder->encode($text);
$encrypted = openssl_encrypt($text,'AES-256-CBC',substr($this->key, 0, 32),OPENSSL_ZERO_PADDING,$iv);
} else {
//獲得16位隨機字串,填充到明文之前
$random = $this->getRandomStr();
$text = $random . pack("N", strlen($text)) . $text . $appid;
// 網路位元組序
$size = mcrypt_get_block_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = substr($this->key, 0, 16);
//使用自定義的填充方式對明文進行補位填充
$pkc_encoder = new PKCS7Encoder;
$text = $pkc_encoder->encode($text);
mcrypt_generic_init($module, $this->key, $iv);
//加密
$encrypted = mcrypt_generic($module, $text);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
$encrypted = base64_encode($encrypted);
}
//print($encrypted);
//使用BASE64對加密後的字串進行編碼
return array(ErrorCode::$OK, $encrypted);
} catch (Exception $e) {
//print $e;
return array(ErrorCode::$EncryptAESError, null);
}
}
/**
* 對密文進行解密
* @param string $encrypted 需要解密的密文
* @return string 解密得到的明文
*/
public function decrypt($encrypted, $appid)
{
try {
if(version_compare(PHP_VERSION, '7','>=')) {
$iv = substr($this->key, 0, 16);
$decrypted = openssl_decrypt($encrypted,'AES-256-CBC',substr($this->key, 0, 32),OPENSSL_ZERO_PADDING,$iv);
} else {
//使用BASE64對需要解密的字串進行解碼
$ciphertext_dec = base64_decode($encrypted);
$module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
$iv = substr($this->key, 0, 16);
mcrypt_generic_init($module, $this->key, $iv);
//解密
$decrypted = mdecrypt_generic($module, $ciphertext_dec);
mcrypt_generic_deinit($module);
mcrypt_module_close($module);
}
} catch (Exception $e) {
return array(ErrorCode::$DecryptAESError, null);
}
try {
//去除補位字元
$pkc_encoder = new PKCS7Encoder;
$result = $pkc_encoder->decode($decrypted);
//去除16位隨機字串,網路位元組序和AppId
if (strlen($result) < 16)
return "";
$content = substr($result, 16, strlen($result));
$len_list = unpack("N", substr($content, 0, 4));
$xml_len = $len_list[1];
$xml_content = substr($content, 4, $xml_len);
$from_appid = substr($content, $xml_len + 4);
if(version_compare(PHP_VERSION, '7','>=')) {
if (!$appid) {
//如果傳入的appid是空的,則認為是訂閱號,使用資料中提取出來的appid
$appid = $from_appid;
}
}
} catch (Exception $e) {
//print $e;
return array(ErrorCode::$IllegalBuffer, null);
}
if ($from_appid != $appid)
return array(ErrorCode::$ValidateAppidError, null);
//不註釋上邊兩行,避免傳入appid是錯誤的情況
if(version_compare(PHP_VERSION, '7','>=')) {
//增加appid,為了解決後面加密回覆訊息的時候沒有appid的訂閱號會無法回覆
return array(0, $xml_content, $from_appid);
} else {
return array(0, $xml_content);
}
}
相關文章
- Laravel 配置微信公眾平臺驗證 token 失敗Laravel
- 微信公眾平臺/擴充套件套件
- 微信公眾平臺基礎框架框架
- 微信公眾平臺開發入門
- 微信API V3 平臺證書解密失敗,返回false,如何解決?API解密False
- PHP微信公眾平臺開發視訊PHP
- 微信公眾號定時群發平臺
- 微信公眾平臺軟文采集專案
- 2.PHP微信公眾平臺開發(二) 公眾平臺示例程式碼分PHP
- 微信公眾平臺開發之店鋪類
- 9.微信公眾平臺開發 - 資料庫操作資料庫
- 1.PHP微信公眾平臺開發(一) 配置介面PHP
- 若比鄰-專注微信公眾號軟文分享平臺
- 微信公眾平臺推文內可以新增檔案了嗎?
- 小程式推廣:微信公眾平臺可自由掛載小程式
- onethink二次開發微信多使用者公眾平臺
- 8.PHP微信公眾平臺開發 自定義選單功能PHP
- 6.PHP微信公眾平臺開發 - 翻譯功能開發PHP
- 3.PHP微信公眾平臺開發 訂閱事件處理PHP事件
- Spring Boot 開發微信公眾號後臺Spring Boot
- 微信公眾平臺清理違規內容 嚴厲打擊公眾號刷量刷粉行為
- 7.PHP微信公眾平臺開發 聊天機器人開發PHP機器人
- 4.PHP微信公眾平臺開發 - 簡單回覆功能開發PHP
- Python開發微信公眾號後臺(系列二)Python
- 微信公眾號開發
- 微信公眾號智慧回答
- 微信公眾號託管
- ThinkJS 接入微信公眾平臺 2 —— 獲取 AccessTokenJS
- 5.PHP微信公眾平臺開發 - 天氣預報功能開發PHP
- 微搜網·微信公眾號大全
- 微信公眾號投票活動製作教程 微信公眾號投票怎麼弄?
- 微信公眾號的留言功能
- Nodejs微信公眾號開發NodeJS
- 本地測試微信公眾號
- .net開發微信公眾號
- 微信公眾號開發-分享
- 微信公眾號介面導讀
- 微信公眾號獲取AccessToekn