1、前言
- AES的加密方式有很多种 CBC、ECB、CTR、OFB、CFB 确认好对方的加密模式还有文档策略,
策略有 “不填充、0填充、空格填充” 这些策略 注意策略的填充也会导致后面的秘钥的生成失败, - AES的密文 随机生成16位AES秘钥,将 业务参数- 转换成的json串加密,加密后的值传入 业务字段中 字段中, 记住算法要跟第三方的一致
- 再然后就是通过对16位AES密钥进行非对称加密 , 通过第三方提供的RSA公钥,进行RSA加密,将加密后的值传入check字段 ,使用的RSA非对称加密,与上面的的AES对称加密是两种不同的加密方式
- 之后上面的加密就完成了 在通过上面的参数生成sign 签名加密
- 签名原串规则 根据统一参数的key值,按照首字母ASCII顺序进行排列,key=value并以&相连
以下为示例
appId=xxx&bizContent=xxx&check=xxx&orgId=xxx&requestId=xxx×tamp=xxx
– 使用自己公司的RSA私钥进行签名,公钥需提供给第三方,签名值传入sign字段, 签名算法 这里要问清楚是什么算法 我这边是SHA256withRSA算法 先前我就是没问清楚算法导致一直有问题
2、示例代码
- 请求示例
<?php
header("content-type:text/html; charset=utf-8");
include "Demo.php";
$model = new DemoController();
$url = "请求地址";
$data = [
"divList"=>[
[
"divRatio"=>"0.4",
"isChargeFee"=>"02",
"mercCode"=>"",
]
],
'divMode'=>"01",
'mercCode'=>"",
'notifyUrl'=>"",
];
$data['divList'] = json_encode($data['divList']);
$postData = $model->response_encrypt($data);
$response = send_post($url, $postData);
function send_post($url, $post_data,$header = 'Content-type:application/x-www-form-urlencoded') {
$postdata = http_build_query($post_data);
$options = array(
'http' => array(
'method' => 'POST',
'header' => $header,
'content' => $postdata,
'timeout' => 15 * 60 // 超时时间(单位:s)
)
);
$context = stream_context_create($options);
$result = file_get_contents($url, false, $context);
return $result;
}
- 类代码示例
<?php
header("content-type:text/html; charset=utf-8");
class DemoController
{
/**
* 测试私钥,正式使用时候需要客户生成秘钥对,使用客户自己的私钥
*/
private $partnersPrivateKey = "";
/**
* 测试公钥,正式使用时,需要向第三方申请获取公钥
*/
private $otherPublicKey = "";
//机构号(咨询客户经理获取)
private $orgId = "";
//应用ID
private $appId = "";
//数据加密
public function response_encrypt($params = [])
{
//要加密的数组
$data = [
"requestId" => "d1bc370c40b848ccad8e325876".mt_rand(1111,9999),
"orgId" => $this->orgId,
"appId" => $this->appId,
"timestamp"=>date("YmdHis", time())
];
//生成aes对称加密密钥
$key = substr(md5(time() . mt_rand(10, 9999)), 0, 16);
//对数据加密
$bizContent = $this->aes_encrypt($params, $key);
$data['bizContent'] = $bizContent;
$data['bizContent'] = str_replace(array("\r\n", "\r", "\n"), "", $data['bizContent']);
//对密钥进行非对称加密(我司公钥)
$publicKey = $this->getPublicKey($this->otherPublicKey, 2);
$secretKey = $this->rsa_public_encrypt($key, $publicKey);
$data['check'] = $secretKey;
$data['check'] = str_replace(array("\r\n", "\r", "\n"), "", $data['check']);
//生成签名
$createsign = $this->sybAscii($data);
//对签名进行非对称加密(贵司私钥)
$privateKey = $this->getPrivateKey($this->partnersPrivateKey, 2);
$sign = $this->rsa_private_encrypt($createsign, $privateKey);
$data['sign'] = $sign;
$data['sign'] = str_replace(array("\r\n", "\r", "\n"), "", $data['sign']);
return $data;
}
//ASCII 加密码
//数组升序排序 =》用&和=拼装成 a=1&b=3&c=ddd格式
public function sybAscii($postData){
$postData = array_filter($postData);
ksort($postData);
$string = [];
foreach ($postData as $key=>$val){
$string[] = $key."=".$val;
}
return implode("&",$string);
}
//数据解密
public function request_decode($params)
{
if (is_string($params)) {
$params = json_decode($params, true);
}
//己方私钥 对方公钥
if (empty($params['sign']) || empty($params['check'])) {
return null;
}
//aes密钥解密
$private_key = $this->getPrivateKey($this->partnersPrivateKey,1);
$secretKey = $this->rsa_private_decode($params['check'],$private_key);
//
$params['iv2'] = $secretKey;
//aes解密
$bizContent = $this->aes_decode($params['bizContent'], $params['check']);
return $bizContent;
}
/**
*验证签名
*sign 对方公钥rsa解密 =》params生成签名 =》 对比sign解密后内容和params签名
* @param string $sing 签名
* @param array $params 密文数组
* @param string $public_key rsa对方公钥
*$return string $sign 签名
*/
public function checkSign($sign, $params)
{
$PublicKey = $this->getPublicKey($this->otherPublicKey, 2);
$sign = $this->rsa_public_decode($sign, $PublicKey);//先解密
$checksign = $this->sybAscii($params);
if (trim($sign) == $checksign) {
return true;
} elseif ($sign == $this->sybAscii($params, 1)) {
return true;
}
return false;
}
/**
* 获取私钥
* @params int $type 1 php 密钥 2密钥需要转换
* @return bool|resource
*/
public 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 密钥 2密钥需要转换
* @return bool|resource
*/
public 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 签名
*/
public 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 签名
* PHP使用openssl进行Rsa加密, 签名算法为 SHA256withRSA
*/
public 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);
openssl_sign($params, $signature, $private_key, OPENSSL_ALGO_SHA256);
$sign = base64_encode($signature);
return $sign;
}
/**
*rsa公钥解密
* @param string $encrypted 密文数组
* @param string $private_key rsa对方公钥
*$return string $sign 签名
*/
public 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 签名
*/
public 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;
$crypto = '';
foreach (str_split(base64_decode($encrypted), 172) as $chunk) {
openssl_private_decrypt($chunk, $decryptData, $private_key);
$crypto .= $decryptData;
}
return $crypto;
}
/**
*aes加密 加密算法采用 AES/CBC/NoPadding.
* @param string $params 明文字符串
* @param string /array $key aes密钥
*$return string 密文 使用的AES的ECB方式加密
*/
public function aes_encrypt($params, $key )
{
if (is_array($params)) {
$params['divList'] = json_encode($params['divList']);
$params = json_encode($params, JSON_UNESCAPED_UNICODE);
} elseif (!is_string($params)) {
return null;
}
$json_bizContent = trim($params);
$encryptStr = base64_encode(openssl_encrypt($json_bizContent, 'AES-128-ECB', $key,OPENSSL_RAW_DATA));
return $encryptStr;
}
/**
*aes解密
* @param array $encrypted 密文
* @param string $key aes密钥
*$return string 文本
*/
public function aes_decode($encrypted, $key)
{
if (!is_string($encrypted)) {
return null;
}
$json_bizContent = openssl_decrypt(base64_decode($encrypted), 'AES-128-ECB', $key,OPENSSL_NO_PADDING);//解密
$json_bizContent = trim(rtrim(rtrim($json_bizContent, chr(0)), chr(7)));
$bizContent = json_decode($json_bizContent, true);
return $bizContent;
}
/**
* 将字符串格式公私钥格式化为pem格式公私钥
* @param $secret_key
* @param $type
* @return string
*/
public 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;
}
}
3、后缀
按照上面的方法亲自测试以OK了,但是有些实际改动需要自己修改 然后再就是AES和最后的签名算法一定要问清楚 不然就会跟我一样用一天的时间填坑