前几天跟某三大运营商之一的机构合作做了个页面,申请了联调接口,不得不说大公司真的是....(形容词自行脑补吧),要个现成的接口走流程都走了两三天。
说到这个加密,又是AES又是RSA,真的好不复杂。
代码贴出来,免得自己又忘记。
首先是AES加密,作为对称性加密。key的话16位或者24位唯一随机字符串就可以了。接口方用得16位,所以我在用32位的时候出现了解密失败。于是demo也用32位的。
AES的类:(接口方放用的是AES/ECB/PKCS5Padding,所以我这里用OPENSSL_PKCS1_PADDING,可以互通,原因不明)
class AES{ public static function encrypt($data, $key) { return base64_encode(openssl_encrypt($data, 'aes-128-ecb', $key, OPENSSL_PKCS1_PADDING));//OPENSSL_PKCS1_PADDING 不知道为什么可以与PKCS5通用,未深究 } public static function decrypt($data, $key) { return openssl_decrypt(base64_decode($data), 'aes-128-ecb', $key, OPENSSL_PKCS1_PADDING);//OPENSSL_PKCS1_PADDING 不知道为什么可以与PKCS5通用,未深究 } }
使用代码:
public function msgkey($data){ $aes = substr(md5(time()),0,16);//16位aes密钥 $rsa = new RSA(); $info = Db::name('info')->where('id=1')->find(); $public_key = $info['public_key']; $aesKey = $rsa->rsaEncrypt($aes,$public_key);//接口方公钥加密aes密钥 if($data!=''){ $m = new AES(); $aesKeyData = $m->encrypt($data, $aes); return $aesKeyData; }else{ return $aesKey; } }
在这一步的时候还遇到了一些问题,就是data传进去的时候有值,但是出来的时候就空了,emmmmm。。。原因忘记了,反正后来是改好了。这个方法是因为那边的接口需要AES加密传送的数据,然后AES密钥又要用RSA加密。。。那么接下来
RSA加密:非对称加密 双方互换公钥保留私钥,网上有很多帮助理解非对称加密的小故事,简单来讲,就是我给你买了个芒果快递给你,快递给你之前呢,我用你给我买的口红(口红是你给我的公钥)在上面画了个爱心,然后签了个名字(Sign),你拿到之后,看到是个画了爱心的芒果,然后你用自己跟口红一起买的,自己偷偷留下的专用卸妆纸(你的私钥)擦了下,掉了,然后你看到了真的芒果,那你怎么判断这个芒果就是我给你寄的呢,你看到了我的sign,验证了一下,嗯,确实是我给你寄的,你就放心的吃了。(大概是这个意思吧)
RSA类:(因为对方没有给我证书文件,所以我直接把对方的公钥跟自己的私钥都存在数据库的)
class RSA { /** * RSA签名 * @param $data 待签名数据 * @param $private_key_path 商户私钥文件路径 * return 签名结果 */ function rsaSign($data) { $info= Db::name('info')->where('id=1')->find(); $priKey = $info['my_prikey']; // $priKey = file_get_contents($private_key_path); $res = openssl_get_privatekey($priKey); openssl_sign($data, $sign, $res,OPENSSL_ALGO_SHA256 ); openssl_free_key($res); //base64编码 $sign = base64_encode($sign); return $sign; } /** * RSA验签 * @param $data 待签名数据 * @param $ali_public_key_path 支付宝的公钥文件路径 * @param $sign 要校对的的签名结果 * return 验证结果 */ function rsaVerify($data, $sign) { $data=strtoupper(md5($data)); $info= Db::name('info')->where('id=1')->find(); $pubKey = $info['hsh_public_key']; $res = openssl_get_publickey("-----BEGIN PUBLIC KEY----- ".$pubKey." -----END PUBLIC KEY-----"); $result = (bool)openssl_verify($data, base64_decode($sign), $res,OPENSSL_ALGO_SHA256); openssl_free_key($res); return $result; } function rsaEncrypt($content){ $info= Db::name('info')->where('id=1')->find(); $pubKey = $info['hsh_public_key']; $res = openssl_get_publickey("-----BEGIN PUBLIC KEY----- ".$pubKey." -----END PUBLIC KEY-----"); // dump($res);exit; //把需要加密的内容,按128位拆开解密 $result = ''; for($i = 0; $i < strlen($content)/128; $i++ ) { $data = substr($content, $i * 128, 128); openssl_public_encrypt ($data, $encrypt, $res); $result .= $encrypt; } $result = base64_encode($result); openssl_free_key($res); return $result; } /** * RSA解密 * @param $content 需要解密的内容,密文 * @param $private_key_path 商户私钥文件路径 * return 解密后内容,明文 */ function rsaDecrypt($content) { $info= Db::name('info')->where('id=1')->find(); $priKey = $info['my_prikey']; $res = openssl_get_privatekey($priKey); //用base64将内容还原成二进制 $content = base64_decode($content); //把需要解密的内容,按128位拆开解密 $result = ''; for($i = 0; $i < strlen($content)/128; $i++ ) { $data = substr($content, $i * 128, 128); openssl_private_decrypt($data, $decrypt, $res); $result .= $decrypt; } openssl_free_key($res); return $result; } }
使用方法:
需要注意的是,公钥跟私钥的前后缀必须要有,不论是文件还是存在数据库,emmmmmm,如果你开心的话,你也可以写死。。。