最近和第三方对接接口,接口需要rsa加密和aes加密。
加密规则
接口应用到的接口加密算法采用 AES/CBC/NoPadding,双方生成rsa秘钥(格式PKCS#8),交互公钥。
接口由密文bigcontent 、签名sign和接口版本信息组成。先随机生成一个aes密钥key(加密向量key(加密向量iv直接截取key的前16位字符,如果key的前16位字符,如果key只用16位那么key和key和iv相等)并对密文bigcontent进行aes加密,然后将aes的密钥$key用对方的rsa公钥加密。然后把所有的接口数据按键名升序排列并用=和&连接(bigcontent=aadfss&key=1234&vesion=2)后md5,最后用自己的私钥对生成的md5字符串进行rsa加密等到数字签名sign。
SMY和合作方各生成一对 RSA 密钥对。保留私钥,将公钥提供给对方。
联合登录接口,合作方用SMY RSA 公钥加密 AES 密钥,用己方 RSA 私钥签名。SMY接收
到请求后,用合作方公钥验签,用SMY私钥解密得到 AES 密钥。
回调接口,SMY用合作方 RSA 公钥加密 AES 密钥,用己方私钥签名。合作方接收到请求后,
用萨摩耶公钥验签,用己方私钥解密得到 AES 密钥。
请求返回示意图:
代码如下
class BaseController
{
private $partnersPublicKey = "";
private $partnersPrivateKey = "";
private $otherPublicKey='';//对方公钥
private $otherPrivateKey = "";
//数据加密
protected function response_encrypt($params=[]){
//要加密的数组
$data=[
'version'=>"3.1",
'timestamp'=>date('Y-m-d H:i:s')];
//生成aes对称加密密钥
$iv = substr(md5(time().mt_rand(10,9999)),0, 16);
//对密钥进行非对称加密(对方公钥)
$publicKey = $this->getPublicKey($this->otherPublicKey,2);
$secretKey = $this->rsa_public_encrypt($iv,$publicKey);
$data['secretKey']= $secretKey;
//对数据加密
$bizContent = $this->aes_encrypt($params,$iv);
$data['bizContent']=$bizContent;
//生成签名
$createsign = $this->createSign($data);
//对签名进行非对称加密(己方私钥)
$privateKey = $this->getPrivateKey($this->partnersPrivateKey,1);
$sign = $this->rsa_private_encrypt($createsign,$privateKey);
$data['sign'] = $sign;
return $data;
}
//数据解密
protected function request_decode($params){
if(is_string($params)){
$params=json_decode($params,true);
}
//己方私钥 对方公钥
if(empty($params['sign'])||empty($params['secretKey'])){
return null;
}
//aes密钥解密
$private_key = $this->getPrivateKey($this->partnersPrivateKey,1);
$secretKey = $this->rsa_private_decode($params['secretKey'],$private_key);
$params['iv2'] = $secretKey;
//aes解密
$bizContent = $this->aes_decode($params['bizContent'],$secretKey);
return $bizContent;
}
/**
*验证签名
*sign 对方公钥rsa解密 =》params生成签名 =》 对比sign解密后内容和params签名
*@param string $sing 签名
*@param array $params 密文数组
*@param string $public_key rsa对方公钥
*$return string $sign 签名
*/
protected function checkSign($sign,$params){
$PublicKey = $this->getPublicKey($this->otherPublicKey,2);
$sign = $this->rsa_public_decode($sign,$PublicKey);//先解密
$checksign = $this->createSign($params);
if(trim($sign)==$checksign){
return true;
}elseif($sign==$this->createSign($params,1)){
return true;
}
return false;
}
/**
*生成签名
*数组升序排序 =》用&和=拼装成 a=1&b=3&c=ddd格式 =》md5字符串
*@param array $params 密文数组
*@param $z=0 1需要转义
*$return string $sign 签名
*/
private function createSign($params,$z=0){
if (is_array($params)) {
ksort($params);
$temp=[];
foreach($params as $k=>$v){
$temp[]=trim($k).'='.trim($v);
}
$str = implode('&',$temp);
if($z==1){
$str = str_replace('/', '\/', $str);
}
return md5($str);
}elseif (!is_string($params)) {
return null;
}
return md5($params);
}
/**
* 获取私钥
*@params int $type 1 php 密钥 2java密钥需要转换
* @return bool|resource
*/
private function getPrivateKey($privateKey,$type=1)
{
if($type==1){
return openssl_pkey_get_private($privateKey);
}elseif($type==2){
return $this->format_secret_key($privateKey,'pri');
}elseif($type==4){
$content = file_get_contents(dirname(__FILE__) . $privateKey);
//echo file_exists(dirname(__FILE__) . $privateKey);
//var_dump(openssl_pkey_get_private($content));exit;
return openssl_pkey_get_private($content);
}
return $privateKey;
}
/**
* 获取公钥
*@params int $type 1 php 密钥 2java密钥需要转换
* @return bool|resource
*/
private function getPublicKey($publicKey,$type=1)
{
//$abs_path = dirname(__FILE__) . '/rsa_public_key.pem';
//$content = file_get_contents($abs_path);
//return openssl_pkey_get_public($content);
if($type==1){
return openssl_pkey_get_public($publicKey);
}elseif($type==2){
return $this->format_secret_key($publicKey,'pub');
}elseif($type==4){
$content = file_get_contents(dirname(__FILE__) . $publicKey);
//echo file_exists(dirname(__FILE__) . $publicKey);
//var_dump(openssl_pkey_get_public($content));exit;
return openssl_pkey_get_public($content);
}
return $publicKey;
}
/**
*rsa用公钥加密
*@param array $params 密文数组
*@param string $private_key rsa对方的公钥
*$return string $sign 签名
*/
private function rsa_public_encrypt($params,$public_key){
if (is_array($params)) {
$params=json_encode($params,JSON_UNESCAPED_UNICODE);
}elseif (!is_string($params)) {
return null;
}
$params = trim($params);
return openssl_public_encrypt($params,$encrypted,$public_key) ? base64_encode($encrypted) : null;
}
/**
*rsa 用私钥加密
*@param array $params 密文数组
*@param string $private_key rsa自己的私钥
*$return string $sign 签名
*/
private function rsa_private_encrypt($params,$private_key){
if (is_array($params)) {
$params=json_encode($params,JSON_UNESCAPED_UNICODE);
}elseif (!is_string($params)) {
return null;
}
$params = trim($params);
return openssl_private_encrypt($params,$encrypted,$private_key) ? base64_encode($encrypted) : null;
}
/**
*rsa公钥解密
*@param string $encrypted 密文数组
*@param string $private_key rsa对方公钥
*$return string $sign 签名
*/
private function rsa_public_decode($encrypted,$public_key){
if (!is_string($encrypted)) {
return null;
}
return (openssl_public_decrypt(base64_decode($encrypted), $decrypted, $public_key)) ? $decrypted : null;
}
/**
*rsa私钥解密
*@param array $encrypted 密文数组
*@param string $private_key rsa对方公钥
*$return string $sign 签名
*/
private function rsa_private_decode($encrypted,$private_key){
if (!is_string($encrypted)) {
return null;
}
return (openssl_private_decrypt(base64_decode($encrypted), $decrypted, $private_key)) ? $decrypted : null;
}
/**
*aes加密 加密算法采用 AES/CBC/NoPadding.
*@param string $params 明文字符串
*@param string/array $key aes密钥
*$return string 密文
*/
private function aes_encrypt($params,$key,$iv=''){
if (is_array($params)) {
$params=json_encode($params,JSON_UNESCAPED_UNICODE);
}elseif (!is_string($params)) {
return null;
}
if(!$iv){
$iv=substr($key,0,16);
}
$json_bizContent = trim($params);
if (strlen($json_bizContent) % 16) {
$json_bizContent = str_pad($json_bizContent,strlen($json_bizContent) + 16 - strlen($json_bizContent) % 16, "\0");
}
$encryptStr = base64_encode(openssl_encrypt($json_bizContent, 'AES-128-CBC', $key, OPENSSL_NO_PADDING, $iv));
return $encryptStr;
}
/**
*aes解密
*@param array $encrypted 密文
*@param string $key aes密钥
*$return string 文本
*/
private function aes_decode($encrypted,$key,$iv=''){
if (!is_string($encrypted)) {
return null;
}
if(!$iv){
$iv=substr($key,0,16);
}
$json_bizContent = openssl_decrypt(base64_decode($encrypted), 'AES-128-CBC', $key, OPENSSL_NO_PADDING, $iv);//解密
$json_bizContent=rtrim(rtrim($json_bizContent,chr(0)), chr(7) );
$bizContent=json_decode($json_bizContent,true);
return $bizContent;
}
/**
* 将字符串格式公私钥格式化为pem格式公私钥
* @param $secret_key
* @param $type
* @return string
*/
private function format_secret_key($secret_key, $type){
// 64个英文字符后接换行符"\n",最后再接换行符"\n"
$key = (wordwrap($secret_key, 64, "\n", true))."\n";
// 添加pem格式头和尾
if ($type == 'pub') {
$pem_key = "-----BEGIN PUBLIC KEY-----\n" . $key . "-----END PUBLIC KEY-----\n";
}else if ($type == 'pri') {
$pem_key = "-----BEGIN RSA PRIVATE KEY-----\n" . $key . "-----END RSA PRIVATE KEY-----\n";
}else{
echo('公私钥类型非法');
exit();
}
return $pem_key;
}
查看原稿:php重写java的aes和rsa加密