技术饭

php7.2微信公众平台消息安全模式的加密及解密

copylian    0 评论    3667 浏览    2020.12.26

php7.2微信公众平台消息安全模式的加密及解密,php7.2发布后新特性吸引了不少PHPer,大家都在讨论新特性带来的好处与便利。但是从php7.0 升级到 php7.2 废弃(过时)了一个在过去普遍应用的扩展(mcrypt扩展)。官方提供了相应的解决提示,却没有提供更详细的解决办法。于是就各种报错,当然了只能改方法了~~~

下面是微信官方提供的消息加密解密算法中的核心部分:文件 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 解密

调整后的代码

/**

* 对明文进行加密

* @param string $text 需要加密的明文

* @return string 加密后的密文

*/

public function encrypt($text, $appid)

{

      try {

            //获得16位随机字符串,填充到明文之前

            $random = $this->getRandomStr(); //"aaaabbbbccccdddd";

            $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);

            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 {

            $iv = substr($this->key, 0, 16);

            $decrypted = openssl_decrypt($encrypted, 'AES-256-CBC', substr($this->key, 0, 32), OPENSSL_ZERO_PADDING, $iv);

      } 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 (!$appid)

                $appid = $from_appid;

            //如果传入的appid是空的,则认为是订阅号,使用数据中提取出来的appid

      } catch (Exception $e) {

            //print $e;

            return array(ErrorCode::$IllegalBuffer, null);

      }

      if ($from_appid != $appid)

            return array(ErrorCode::$ValidateAppidError, null);

        //不注释上边两行,避免传入appid是错误的情况

        return array(0, $xml_content, $from_appid);

        //增加appid,为了解决后面加密回复消息的时候没有appid的订阅号会无法回复

}

参考文档:https://blog.csdn.net/sapperlab/article/details/56672443

飓风呀
感谢你的支持,我会继续努力!
扫码打赏,感谢您的支持!

文明上网理性发言!

  • 还没有评论,沙发等你来抢