PHP对接第三方接口,数据加密 使用AES和RSA方式

1、前言

  1. AES的加密方式有很多种 CBC、ECB、CTR、OFB、CFB 确认好对方的加密模式还有文档策略,
    策略有 “不填充、0填充、空格填充” 这些策略 注意策略的填充也会导致后面的秘钥的生成失败,
  2. AES的密文 随机生成16位AES秘钥,将 业务参数- 转换成的json串加密,加密后的值传入 业务字段中 字段中, 记住算法要跟第三方的一致
  3. 再然后就是通过对16位AES密钥进行非对称加密 , 通过第三方提供的RSA公钥,进行RSA加密,将加密后的值传入check字段 ,使用的RSA非对称加密,与上面的的AES对称加密是两种不同的加密方式
  4. 之后上面的加密就完成了 在通过上面的参数生成sign 签名加密
  • 签名原串规则 根据统一参数的key值,按照首字母ASCII顺序进行排列,key=value并以&相连
    以下为示例
    appId=xxx&bizContent=xxx&check=xxx&orgId=xxx&requestId=xxx&timestamp=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和最后的签名算法一定要问清楚 不然就会跟我一样用一天的时间填坑

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杰姆小生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值