最近和第三方对接接口,接口需要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 密钥。



请求返回示意图:


iOS aes加密解密 aes加密api_json

 

代码如下

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;
  }