前几天跟某三大运营商之一的机构合作做了个页面,申请了联调接口,不得不说大公司真的是....(形容词自行脑补吧),要个现成的接口走流程都走了两三天。
说到这个加密,又是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-----\n".$pubKey."\n-----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-----\n".$pubKey."\n-----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,如果你开心的话,你也可以写死。。。