文档章节

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

豆花饭烧土豆
 豆花饭烧土豆
发布于 2017/06/02 00:22
字数 1205
阅读 87
收藏 1

php7.1发布后新特性吸引了不少PHPer,大家都在讨论新特性带来的好处与便利。但是从php7.0 升级到 php7.1 废弃(过时)了一个在过去普遍应用的扩展(mcrypt扩展)。官方提供了相应的解决提示,却没有提供更详细的解决办法。于是坑来了….

下面是微信官方提供的消息加密解密算法中的核心部分

/**
 * 对明文进行加密
 * @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的订阅号会无法回复
}

参考资料

http://blog.csdn.net/sapperlab/article/details/56672443

php官方对mcrypt扩展被废弃的说明

微信官方对于消息加密的接入指引

OpenSSL实现对称加密AES和非对称加密RSA.

AES:
<?php
header('Content-Type: text/plain;charset=utf-8');
$data = 'phpbest';
$key = 'oScGU3fj8m/tDCyvsbEhwI91M1FcwvQqWuFpPoDHlFk='; //echo base64_encode(openssl_random_pseudo_bytes(32));
$iv = 'w2wJCnctEG09danPPI7SxQ=='; //echo base64_encode(openssl_random_pseudo_bytes(16));
echo '内容: '.$data."\n";

$encrypted = openssl_encrypt($data, 'aes-256-cbc', base64_decode($key), OPENSSL_RAW_DATA, base64_decode($iv));
echo '加密: '.base64_encode($encrypted)."\n";

$encrypted = base64_decode('To3QFfvGJNm84KbKG1PLzA==');
$decrypted = openssl_decrypt($encrypted, 'aes-256-cbc', base64_decode($key), OPENSSL_RAW_DATA, base64_decode($iv));
echo '解密: '.$decrypted."\n";
?>

RSA:
用openssl生成rsa密钥对(私钥/公钥):
openssl genrsa -out rsa_private_key.pem 1024
openssl rsa -pubout -in rsa_private_key.pem -out rsa_public_key.pem
<?php
header('Content-Type: text/plain;charset=utf-8');
$data = 'phpbest';
echo '原始内容: '.$data."\n";

openssl_public_encrypt($data, $encrypted, file_get_contents(dirname(__FILE__).'/rsa_public_key.pem'));
echo '公钥加密: '.base64_encode($encrypted)."\n";

$encrypted = base64_decode('nMD7Yrx37U5AZRpXukingESUNYiSUHWThekrmRA0oD0=');
openssl_private_decrypt($encrypted, $decrypted, file_get_contents(dirname(__FILE__).'/rsa_private_key.pem'));
echo '私钥解密: '.$decrypted."\n";
?>

参考:

https://segmentfault.com/q/1010000007210963/a-1020000007213233

© 著作权归作者所有

共有 人打赏支持
豆花饭烧土豆
粉丝 15
博文 355
码字总数 93005
作品 0
深圳
私信 提问
加载中

评论(1)

烟雨苏苏
烟雨苏苏
博主在么?麻烦请教个问题
微信消息体签名及加解密功能详细解析以及.net实现

原文:微信消息体签名及加解密功能详细解析以及.net实现 前言 微信消息体签名及加密功能已上线,明文传输确实存在安全风险,鉴于微信的用户范围使用之广泛,必定会成为众矢之的。所以大家还是...

杰克.陈
2015/02/02
0
0
Java微信公众平台开发_02_启用服务器配置

一、准备阶段 需要准备事项: 1.一个能在公网上访问的项目: 见:【 Java微信公众平台开发01本地服务器映射外网 】 2.一个微信公众平台账号: 去注册:(https://mp.weixin.qq.com/) 3.策略...

rayner
2017/11/01
0
0
微信公众平台开发(1)-接入指南

接入指南 第一步:申请消息接口 登录https://mp.weixin.qq.com/ 后,在公众平台后台管理页面 – 开发者中心页,点击“修改配置”按钮,填写URL、Token和EncodingAESKey, 其中URL是开发者用来...

当时我就震惊啦
2014/11/26
0
0
Java微信公众平台开发_03_消息管理之被动回复消息

GitHub源码:https://github.com/shirayner/weixin_gz 一、本节要点 1.回调url 上一节,我们启用服务器配置的时候,填写了一个服务器地址(url),如下图,这个url就是回调url,是开发者用来...

rayner
2017/11/13
0
0
JFinal Weixin 1.1 发布,微信极速 SDK

JFinal Weixin 1.1 主要添加了消息加密功能,以及对 access token 意外失效问题做出了处理。 access token 通常会因为外部原因而意外失效: 1:项目中 AccessTokenApi 在获取到 access token...

JFinal
2014/12/04
9.6K
17

没有更多内容

加载失败,请刷新页面

加载更多

springboot2.0 rabbitmq

AmqpTemplate接口没有参数说明,不知道传啥参数,这是很难受,不知道怎么看参数的定义.... 1、先来添加引用包 <dependency> <groupId>org.springframework.boot</groupId> <artifa......

朝如青丝暮成雪
13分钟前
1
0
replace File.separator出现异常:【java.lang.IllegalArgumentException: character to be escaped is missing】

写代码时,要写这么一个功能:将包名中的"."换成路径分隔符("/"或"",视操作系统而定),要求在window或linux下都能正常运行。 分析一下,这明显是个字符串替换的操作,可用使用String提供的...

Funcy1122
15分钟前
1
0
centos7下查找项目路径

1、查出tomcat的端口(如已知端口请忽略,其实用该命令也可以查出tomcat的位置,项目太多,不想找的话,就用下面的步骤吧。) ps -aux | grep tomcat 2、根据端口号查询进程,并获取进程ID ...

You_are_my_music
17分钟前
1
0
微信小程序与原生的App(iOS、Android)的优势对比

小程序也属于App的一种,那么它和我们现在流行的原生App(IOS、Android)相比,有什么区别和优势呢? 一、区别与优势 首先,从技术上来讲,目前App的主流开发方式有三种:Web App 、Native ...

Mr_ET
19分钟前
1
0
第二篇:SpringBoot接口Http协议

1、SpringBoot HTTP请求配置 简介:SpringBoot2.xHTTP请求注解讲解和简化注解配置技巧 1、@RestController and @RequestMapping是springMVC的注解,不是springboot特有的 2、@RestController...

嘴角轻扬30
20分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部