微信API V3 平台证书解密失败,返回false,如何解决?
安装官方文档说明的,要先获取平台证书列表,然后再进行解密,获取方法如下:
图片中User-Agent后面备注的用户代理(https://zh.wikipedia.org/wiki/User_agent)不知道是干嘛的..
按照之前的签名方法,最后获得Authiorization值,放到header头中,header头参数如下:
$headers[] = 'Content-Type:application/json';
$headers[] = 'Accept:application/json';
$headers[] = 'User-Agent:'.$_SERVER['HTTP_USER_AGENT'];
$headers[] = 'Authorization:WECHATPAY2-SHA256-RSA2048 '.$token;
获得证书列表如下:
为方便后面说明问题,我这里把data数据整个贴出来
{
"data": [
{
"effective_time": "2020-08-20T14:59:52+08:00",
"encrypt_certificate": {
"algorithm": "AEAD_AES_256_GCM",
"associated_data": "certificate",
"ciphertext": "io6eoAWJH1rb8GJ04aJ6LBUu059j2VhWpoi3Odaez1ngOHVdNPH4bHRLRji6Bdb3QTYWgF0L9Zjdjc/Zbt5dC10ixd8KlB1ZvRDaJ4nSSmVdJ0L92i3ORHJmKHsWVbt8+ZaM2Kg+vfDLRqnoZ4/YnL4PPi/U5bXovvFIzvOcvb6u6hXdq8+Ad7Ms4iOilKJyOiDNMci7HlC+h4elp5UvbebITMEG4OdzexHi8PEcrcRYEODFygzd0SDvOj5sQGjRTqtpA71o55gD0AKTtud3SH856y/N/2GIcmHArB7U5Q5zXQQQ8Fz14KA5bas7snqMMlqv/6c4aFsQjA1Di2Dm1+/E2aMHWn4UpDAohV4X5+MSHHfDdtsIfnvsgKwLjzTKXSe2d/FEjPYgtf/p6GwptGTJFbrk4octETAMpVIE8c2lUq6E8ekf+PqCYLT5njin8z6CBK/7PC4DrR195vTf/pOikyEV6fqc3/iety39y2LCgvDzL/0rz40thvHz4uBNqaxtnTo2SJlouR4D3qi+dmjO4XMWyPAZXpxxboV9PCQAufSGF65tSO8pRGPZiD76mNCyRi8nYeZtL3HFg4afXZeObAzQMJnxaMRsn7jObS+pibgbK45oUfmuoq8yEBUuvqvf5O3JPqKyI4QVqmYg0H07U9ueCWR/iHlsEsSwMG9Hjy4SvW5wPGYGdW9mio21YOl1uucTkS2b+szILnSaJ3oWoTb417qjoGI6Bt6ZWRRZAOmJsJGkzDTv1din0Wg/vRUpFFS/HI466E6yjEbORC8sPL1oze9qx2KsgML6GcUABdWcUXXqzScXxhFufeIkdqj2hG+I+ElNwlpuY/FKd6pqM29BP5up+EtazsABMjEb3e+WHbrwzmzY5c+uRwozd/Z15SKjYtgzOTK0HKbmyOBccA+5moFIt42Alwci3UIUYc5KStdywT42iCc++FtE+Dzu5R0spFFnEj9g9gbw8HzWWL2SCHoYOSsyFegz1QGYiJYAILdIle0IyFD2+PZGNbcdXzE6y2VS6GREhnRmn6QMfb83L7xUTQQf43NdsgXLb+XHGwtn0tZqH+vsMKn1hi0wmelqIsxHv+CvFKg/f7HoRd52vZ6i+uIfXE2nR1dHbygMtlZan47D45g3ZT+lI+0ECojFQI9IuGxRP35n3Sv4yjEvSIbs3+BTEYWZs6gmCyyK2vIm58j8TkTtuyGC5s4ExKl2o3Qfw1mUUg6WKzfnc4iup9Q8JsRNyH+/ra4FvATODvRZakAqlsaV079E+FRu73D+4lq92ZBST/XnGW2jrxuOxGsI2Q9AHiOZWoIFXVZp8DtBaFj/hGEAgN0atv53At/KdrM5Y+x7zx6v9NGOKkFnkw7LuJsARwj6pVSoh2cOQ9tJzCeeSQF985+q66Bmq6Kq51oMnyEzMe+HoOxeUEcTvLfCwteAcJzG/AkZwkwvnXLCcRBpc4ZEEfJtzwSasqdYapE/jexHG1fCQXcuqtdJUeXtxIBBMeTwLEBwed6pGjNw1tmDyNRN2fOzPX7FXbjgek8CJZC4oSStp7tuMIYkoV0RbmDBiSXV0QdOQ5vXgZw4WPalWv32gYhXG8yg8EE6MbPP4Pl8TpFm1CfCpWz2MVS5PyzY/2+lMfGuE3QK5IURbbZsILZJSY6e9TS9stquZEm4hnYB1syzTIf2+dL3GoFOQ45risIUcO68pTo4uBvKm2elpYTtiiQuwxj+0aKwLAilCGy3khNEdZfreWQaTuM///Pu5CkuBbCCYoevJEQnvX7U1HWRDJ1BjVE5Dqc23m2ThQ4exgihvikA4D2RnvGMpBhSJ71kD1kuPMCw+pQLvPFTKS/lOsH9cDZR+Gulvk29TTZ01EtjaQCbwrn6YQ==",
"nonce": "aacc84fbf399"
},
"expire_time": "2025-08-19T14:59:52+08:00",
}
]
}
下面开始看官方文档里证书和回调报文解密的部分,看到官方有提供整个解密操作的php代码,这里也全部贴出来,如下:
class AesUtil{
/**
* AES key
*
* @var string
*/
private $aesKey;
const KEY_LENGTH_BYTE = 32;
const AUTH_TAG_LENGTH_BYTE = 16;
/**
* Constructor
*/
public function __construct($aesKey)
{
if (strlen($aesKey) != self::KEY_LENGTH_BYTE) {
throw new InvalidArgumentException('无效的ApiV3Key,长度应为32个字节');
}
$this->aesKey = $aesKey;
}
/**
* Decrypt AEAD_AES_256_GCM ciphertext
*
* @param string $associatedData AES GCM additional authentication data
* @param string $nonceStr AES GCM nonce
* @param string $ciphertext AES GCM cipher text
*
* @return string|bool Decrypted string on success or FALSE on failure
*/
public function decryptToString($associatedData, $nonceStr, $ciphertext)
{
$ciphertext = \base64_decode($ciphertext);
if (strlen($ciphertext) <= self::AUTH_TAG_LENGTH_BYTE) {
return false;
}
// ext-sodium (default installed on >= PHP 7.2)
if (function_exists('\sodium_crypto_aead_aes256gcm_is_available') &&
\sodium_crypto_aead_aes256gcm_is_available()) {
return \sodium_crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
}
// ext-libsodium (need install libsodium-php 1.x via pecl)
if (function_exists('\Sodium\crypto_aead_aes256gcm_is_available') &&
\Sodium\crypto_aead_aes256gcm_is_available()) {
return \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $associatedData, $nonceStr, $this->aesKey);
}
// openssl (PHP >= 7.1 support AEAD)
if (PHP_VERSION_ID >= 70100 && in_array('aes-256-gcm', \openssl_get_cipher_methods())) {
$ctext = substr($ciphertext, 0, -self::AUTH_TAG_LENGTH_BYTE);
$authTag = substr($ciphertext, -self::AUTH_TAG_LENGTH_BYTE);
$rs = \openssl_decrypt($ctext, 'aes-256-gcm', $this->aesKey, \OPENSSL_RAW_DATA, $nonceStr,
$authTag, $associatedData);
var_dump($rs);exit; // 打印出解密结果
return $rs;
}
throw new \RuntimeException('AEAD_AES_256_GCM需要PHP 7.1以上或者安装libsodium-php');
}
}
这个类很简单,主要是用到3个参数:$associatedData, $nonceStr, $ciphertext
,还有一个初始化类的时候用到的API V3的秘钥,这个是在商户后台的API安全里配置的。
我直接把官方提供的这个类原封不动的拷过来,然后在控制器方法中引用这个类:
$aesutil = new AesUtilController($apiv3Key);
$rs = $aesutil->decryptToString($associatedData,$nonce,$ciphertext);
四个参数:
$apiv3Key 我在商户后台-API安全里配置的秘钥;
$associatedData 就是上面data数据里的associated_data值”certificate”;
$nonce 就是上面data数据里的nonce值”aacc84fbf399”;
$ciphertext 就是上面data数据里的nonce值”ciphertext”;
全部配置好后,运行测试,返回bool(false)
,解密失败!
想不明白,问题出在哪里,一共就4个参数,$apiv3Key是固定值,不会有问题,其他3个参数都是从平台api获取的,应该也不会错的啊?
尝试在线base64解码”ciphertext”的值,结果是乱码:
一脸懵逼!不知道是本来就应该是乱码还是我获取的”ciphertext”值是有问题的?又或者是我中间漏了什么操作?
又研究了一下官方的代码,看到sodium_crypto_aead_aes256gcm_decrypt
这个方法不能使用,于是又百度,看到需要开启php.ini里的extension=php_sodium.dll
,发现里面没有这行代码,于是直接写进去了,再到php安装目录下查看libsodium.dll
文件是存在的,再到ext
目录下检查php_sodium.dll
文件也在存在的,最后phpinfo(),看到sodium
配置成功了。
再次尝试运行,仍然返回bool(false)
,解密还是失败!
至此,我基本是没得办法了。。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: