PHP安全——AES&RAS加密方案与实战

具体过程是先由接收方创建RSA密钥对,接收方通过Internet发送RSA公钥到发送方,同时保存RSA私钥。而发送方创建AES密钥,并用该 AES密钥加密待传送的明文数据,同时用接受的RSA公钥加密AES密钥,最后把用RSA公钥加密后的AES密钥同密文一起通过Internet传输发送 到接收方。当接收方收到这个被加密的AES密钥和密文后,首先调用接收方保存的RSA私钥,并用该私钥解密加密的AES密钥,得到AES密钥。最后用该 AES密钥解密密文得到明文。

服务器端进行请求响应时将上面流程反过来即可,具体实现流程可做优化,例如:client将encryptkey传输给服务器端的方式可以通过HttpHeader来实现。

RSA

非对称加密,公钥加密,私钥解密,反之亦然。由于需要大数的乘幂求模等算法,运行速度慢,不易于硬件实现。

通常私钥长度有512bit,1024bit,2048bit,4096bit,长度越长,越安全,但是生成密钥越慢,加解密也越耗时。

既然是加密,那肯定是不希望别人知道我的消息,所以只有我才能解密,所以可得出公钥负责加密,私钥负责解密;

  • 同理,既然是签名,那肯定是不希望有人冒充我发消息,只有我才能发布这个签名,所以可得出私钥负责签名,公钥负责验证。
1. 加密SecretKey

使用RSA 公钥(publicKey)加密AES SecretKey,将得到的加密数据经过Base64编码得到一个UTF-8字符串。将此结果字符串作为HTTP POST方法请求头的requestId字段值

2. 解密SecretKey

使用RSA 私钥(privateKey)加密请求头中的requestIdBase64字符串,得到AES SecretKey的原始字节数据,并重新构建成SecretKey。
请求响应流程

1. 发起请求

客户端准备请求头(appId、requestId)、请求体信息(requestBody),通过HTTP POST方法向服务端发起请求。

2. 服务端验证

服务端接收到客户端请求,首先获取appId,判断appId是否有效;如果appId无效或requestId为空,立即响应未授权信息(HttpStatus:401)。
服务端对requestId做Base64解码,通过appId对应的RSA 私钥(privateKey)解密Base64解密结果,得到AES SecretKey。
服务端读取请求体重的Base64字符串,并进行Base64解码,通过AES SecretKey解密
Base64解密结果,得到原始请求的JSON的UTF-8字符串。
在解密过程中因为requestId的不合法情况,服务端立即响应拒绝请求信息(HttpStatus:403)。

3. 服务端响应

服务端成功处理业务后,将响应结果转换成JSON的UTF-8字符串,通过AES SecretKey加密得到Base64字符串,将此字符串作为请求响应结果。

4. 响应客户端

客户端接收到服务端成功响应(HttpStatus:200),获取响应结果(加密后的Base64字符串)。经过Base64解码,通过AES SecretKey解密,将解密后的数据转换为UTF-8格式的字符串,得到服务端响应的原始JSON字符串。

AES

对称加密,密钥最长只有256个bit,执行速度快,易于硬件实现。由于是对称加密,密钥需要在传输前通讯双方获知。

基于以上特点,通常使用RSA来首先传输AES的密钥给对方,然后再使用AES来进行加密通讯。

1. 加密请求信息

为每个请求生成一个独立的AES SecretKey,使用AES算法,通过SecretKey加密请求数据的JSON字符串,将得到的加密数据经过Base64编码得到一个UTF-8字符串。将此结果字符串作为HTTP POST方法的请求主体(RequestBody)。

2. 解密响应结果

使用AES SecretKey解密服务端成功响应的Base64字符串,解密后得到服务端响应的JSON字符串。

请求流程:
  1. 服务器端(server)和客户端(client)分别生成自己的密钥对
  2. server和client分别交换自己的公钥
  3. client生成AES密钥(aesKey)
  4. client使用自己的RSA私钥(privateKey)对请求明文数据(params)进行数字签名
  5. 将签名加入到请求参数中,然后转换为json格式
  6. client使用aesKey对json数据进行加密得到密文(data)
  7. client使用sever的RSA公钥对aesKey进行加密(encryptkey)
  8. 分别将data和encryptkey作为参数传输给服务器端
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";
?>

用openssl生成rsa密钥对(私钥/公钥):

openssl genrsa -out rsa_private_key.pem 2048
openssl rsa -pubout -in rsa_private_key.pem -out rsa_public_key.pem
RSA:
<?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";
?>

通用协议

通讯协议     HTTP
请求方法     POST
请求URL     http://host:port/sdk
授权验证     appId: 请求头,客户端唯一标识(无需加密) requestId: 请求头,请求唯一标识(使用RSA加密)
请求数据     请求数据在HTTP请求主体中传递(使用AES加密)
加密算法     RSA 与 AES 结合 (AES/ECB/PKCS5Padding)
字符编码     Base64、UTF-8

请求实例

证书信息
appId:
test
publicKey:
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDEBflTi1uIdpSLZdpTpwjtPu+xMKi6OlzdcZqzRaR0NnCNYI1NlkNi5sMIm7CRSVymvfifFpd1Y9GHnqLmW9r4dfiHzQpn75KDnsMODNNKuHiHTNt+JFOxwmn2eJ1bJJhUaPs/vSbe+2L113nZ/PK9PnwLlzv09prJICv4LjfMwwIDAQAB
privateKey:
MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAMQF+VOLW4h2lItl2lOnCO0+77EwqLo6XN1xmrNFpHQ2cI1gjU2WQ2LmwwibsJFJXKa9+J8Wl3Vj0YeeouZb2vh1+IfNCmfvkoOeww4M00q4eIdM234kU7HCafZ4nVskmFRo+z+9Jt77YvXXedn88r0+fAuXO/T2mskgK/guN8zDAgMBAAECgYAWBqXRYWNTnh9q2nMB208BeYTtYA5VfqFYsJ81bang9Q0IlcTK5wuDXXKk0PmyGZAznD0Hgz5ZjnHIz2Z2g/PCrMVt+ygGNi/Ic7dOcEBUCU8lNgBQ9Rb0rDy4FAyei0moIBuX5mm5l7664B4ctKGaD/ZTnT1lpBiI1m6NkpT0AQJBAPfKc/ovQZcJs4CwizasEBHKFU20BOanF0Q2KXXQMEfUVeJwMmRIAfaNg5X/R+Y+p3vIRdbzCvlzzu9b8yGPyUMCQQDKhHlQNr1IVbAm+u+Xwq60ee8bMK/6yreVYEmTz7glNMnDLk1htq0iBinZJz+IpfJi4UIUy4aDQ7qttHyY3/aBAkEAvpXZ5FkKgsg0Z0vil5d5+KTNA+ntRxbHf3CFLReGuwinUjXnnFgCIoev6OPjgkU4fq2Igg6Hp1wakuHPPEYijwJAdxpH+nvnADac3Z/w7tDpTGz1yYjtxlIYe/pf61VbmE4eZvLBQblcw0jarcKeY/AsyChwY6h62JIppOUj7gixgQJBAIK4zPkJhiiFZYrjkHp8JXjprLbyTFL/FpwQTB4l53qXdLZXroymFZFhN3eWXMdcwGwC+vNFva1OC2bPDf2RA44=
发送请求

AES SecretKey对应的Base64字符串:

cbkV1CTPzgC7K8u1485GNg==
原始请求体JSON字符串:
{“val1”:1,”val2”:5}
请求信息
{
Headers: {
appId: "test",
requestId: "KUct+A/Ukkssnz4zBXI7TG+fMIJeLGsudtbz96s3BIk5fPAFxIkbGIwNmc5UcmUXk5lJ+6jk1gT7pPiYoE7ksafKFuxO8eZF7sWII/dPkxOIfgMOC2ZEp84IoGwEYJiWkUQsnHcMNiFixQySZo7bNGre5rzTD7mjLkjMRQLqetA="
},
requestBody: "Bsa3B8/HtY2KGdQu6P5Itt8ZIAOzwtVA24VW21oIUaA="
}
服务端响应

原始JSON字符串:

{“code”:200,”result”:6}
客户端实际接收到的结果(Base64字符串):
h5nN/sAshI7iQ0ES5AMT+vRcEmysGBeV+WAyuw0B1lk=
PHP加密验证代码
/**
* 获取honor私钥资源id
*/ 
if(!function_exists('pi_key')){ 
    function pi_key($from = "honor") { 
        return openssl_pkey_get_private(file_get_contents(
        '../cert/'.$from.'/cert_private.pem')); 
    } 
} 

/**
* 获取partner公钥资源id
*/ 
if(!function_exists('pu_key')){ 
function pu_key($to = "partner") { 
return openssl_pkey_get_public(file_get_contents(
    '../cert/'.$to.'/cert_public.key')); 
    } 
} 

/**
* rsa私钥加密
*/ 
if(!function_exists('pi_encrypt')){ 

/**
* @param $data
* @param string $use
* @return string
*/ 
function pi_encrypt($data,$use = 'honor') { 
    openssl_private_encrypt($data,$encrypt,pi_key($use)); 
        return base64_encode($encrypt); 
    } 
} 

/**
* rsa私钥解密
*/ 
if(!function_exists('pi_decrypt')){ 
/**
* @param $data
* @param string $use
* @return mixed
*/ 
function pi_decrypt($data,$use = 'partner'){ 
    openssl_private_decrypt(base64_decode($data),$decrypt,pi_key($use)); 
        return $decrypt; 
    } 
} 

/**
* rsa公钥解密
*/ 
if(!function_exists('pu_decrypt')){ 
/**
* @param $data
* @param string $use
* @return mixed
*/ 
function pu_decrypt($data,$use = "honor") { 
    openssl_public_decrypt(base64_decode($data),$decrypt,pu_key($use)); 
        return $decrypt; 
    } 
} 

/**
* rsa 公钥加密
*/ 
if(!function_exists('pu_encrypt')){ 
/**
* @param $data  $data  要加密的数据
* @param string $use    用哪个公钥
* @return string
*/ 
function pu_encrypt($data,$use = "partner") { 
    openssl_public_encrypt($data,$encrypt,pu_key($use)); 
        return base64_encode($encrypt); 
    } 
} 

/**
* aes 加密
*/ 
if(!function_exists('aes_encrypt')){ 
    /**
    * @param $key  私钥
    * @param $input 要加密的数据
    * @return string
    */ 
    function aes_encrypt($key,$input) { 
            $data = is_array($input)? json_encode($input,JSON_UNESCAPED_UNICODE):$input; 
            return openssl_encrypt($data,'aes-128-ecb',$key); 
    } 
} 

/**
* aes 解密
*/ 
if(!function_exists('aes_decrypt')){ 
    /**
    * @param $key  私钥
    * @param $data 要解密的数据
    * @return bool|string
    */ 
    function aes_decrypt($key,$data) { 
        return json_decode(openssl_decrypt($data,'aes-128-ecb',$key),true)?:json_decode($data,true); 
    } 
} 

/**
* 连接半半
*/ 
if(!function_exists('connect_partner')){ 
    /**
    * @param $raw_data    post  过去的数据
    * @param string   $action   接口名
    * @return mixed
    */ 
    function connect_partner($raw_data,$action = "addition") { 
        //aes 随机私钥(16位重要) 
        $aes_key = str_random(16); 
        //rsa 公钥加密 
        $requestId = pu_encrypt($aes_key); 
        //aes 加密后数据 
        $aes_data = aes_encrypt($aes_key,$raw_data); 
        //请求地址 
        $url = "192.168.0.131:8088/sdk/".$action; 
        //请求头 
        $header = ["appId:test","requestId:$requestId","Content-Type:application/json"]; 
        //curl 
        $ch = curl_init($url); 
        curl_setopt($ch, CURLOPT_HTTPHEADER,$header); 
        curl_setopt($ch, CURLOPT_HEADER, 0); 
        curl_setopt($ch, CURLOPT_TIMEOUT,1); 
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 
        curl_setopt($ch, CURLOPT_POST, 1); 
        curl_setopt($ch, CURLOPT_POSTFIELDS,$aes_data);
        $out_put = curl_exec($ch) or die('远程主机连接失败!'); 
        return aes_decrypt($aes_key,$out_put); 
    } 
}

PS:关于加密解密感兴趣的朋友还可以参考本站在线工具:

在线RSA加密/解密工具:

文字在线加密解密工具(包含AES、DES、RC4等):

在线散列/哈希算法加密工具:

在线MD5/hash/SHA-1/SHA-2/SHA-256/SHA-512/SHA-3/RIPEMD-160加密工具:

在线sha1/sha224/sha256/sha384/sha512加密工具:

坚持原创技术分享,您的支持将鼓励我继续创作!