• PHP 实现 SHA256 with RSA 签名 (实例讲解)


    背景

    • 近期在对接 美餐支付 接口文档时,
      重点需根据 sha256WithRSA 签名规则,进行加密处理
      通过参考网上的签名经验,最后整理出适合自己业务使用的处理方法
      欢迎各位指摘 …

    实现方式

    • 签名加密、解密代码:
        /**
         * @Notes:生成 sha256WithRSA 签名
         * 提示:SPKI(subject public key identifier,主题公钥标识符)
         * @param null $signContent     待签名内容
         * @param string $privateKey    私钥数据(如果为单行,内容需要去掉RSA的标识符)
         * @param bool $singleRow       是否为单行私钥-标识
         * @return string               签名串
         * @User: zhanghj
         * @DateTime: 2023-09-27 9:41
         */
        public function getSHA256SignWithRSA($signContent = null, $privateKey = '', $singleRow = false){
            if ($singleRow){
                //如果传入的私钥是单行数据,且没有RSA的标识符,需做格式转化
                $privateKey = "-----BEGIN RSA PRIVATE KEY-----\n" .
                              wordwrap($privateKey, 64, "\n", true) .
                              "\n-----END RSA PRIVATE KEY-----";
            }
            $key = openssl_get_privatekey($privateKey);
            //开始加密
            openssl_sign($signContent, $signature, $key, OPENSSL_ALGO_SHA256);
            //进行 base64编码 加密后内容
            $encryptedData = base64_encode($signature);
            openssl_free_key($key);
            return $encryptedData;
        }
    
        /**
         * @Notes:验证 sha256WithRSA 签名
         * @param null $signContent     待签名内容
         * @param string $signatureStr  签名串
         * @param string $public_key    公钥数据(如果为单行,内容需要去掉RSA的标识符)
         * @param bool $singleRow       是否为单行私钥-标识
         * @return int                  1:签名成功,0:签名失败
         * @User: zhanghj
         * @DateTime: 2023-09-27 10:38
         */
        public static function verifySha256SignWithRSA($signContent = null, $signatureStr = '', $public_key = '',$singleRow = false)
        {
            if ($singleRow){
                $public_key = "-----BEGIN PUBLIC KEY-----\n" .
                    wordwrap($public_key, 64, "\n", true) .
                    "\n-----END PUBLIC KEY-----";
            }
            $key = openssl_get_publickey($public_key);
            $ok = openssl_verify($signContent, base64_decode($signatureStr), $key, OPENSSL_ALGO_SHA256);
            openssl_free_key($key);
            return $ok;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 签名加密,操作举例
    /**
     * 1. 如果得到的 私钥数据,没有RSA标识符,此时,要求私钥为【单行】形式
     */
    $signature_res = self::getSHA256SignWithRSA($sign_str,$this->private_key,true);
    
    /**
     * 2. 如果得到的 私钥数据,拥有RSA标识符,此时,要求私钥为标准的形式(每行64个字符)
     */
    $signature_res = self::getSHA256SignWithRSA($sign_str,$this->private_key,false);
    
    /**
     * 3. 如果得到的 私钥数据,是以 pem文件形式存储,此时,需先加载指定目录的 pem文件
     */
    // 加载私钥文件
    $this->private_key = openssl_pkey_get_private(file_get_contents(self::KEY_FILE_DIR.'rsa_private.pem'));        
    $signature_res = self::getSHA256SignWithRSA($sign_str,$this->private_key,false);
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    附录

    ① . 参考文章

    ②. GuzzleHttp 使用技巧

      1. 实例化类 Client

    配置参数 http_errors 可解决 401 授权问题

    //实例化 Client
    $httpClient = new Client([
                'base_uri' => self::BASE_URL,
                'verify' => false,
                'http_errors' => false]);
    
    //请求传参            
    $options = [
                    'timeout' => 5,
                    //传输 headers
                    'headers' => $header_data,
                ];
                
    //判断请求方式
    if ($request_method == 'GET'){
        $options['query'] = $request_body;
    }else{
        //参数需在请求JSON传参
        //$options['form_params'] = $request_body;
        $options['json'] = $request_body;
    }
    
    //请求目标接口,处理反馈信息
    $response  = $this->httpClient->request($request_method,$url,$options);
    $contents = $response->getBody().'';
    $opData = json_decode($contents,true);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    . 签名提示

      1. 如果使用了 json_encode($arr,JSON_UNESCAPED_UNICODE) 类似编码,目的是为了不让中文乱码
        但是,有的签名验证会因为这个原因,提示签名错误
        原因在于:签名前的数据,和签名后的数据 在处理时可能因为编码不一致,造成识别有误

    启发来源:美餐支付 作退款接口开发时,因为这个原因造成 耗时排查!

  • 相关阅读:
    力扣 -- 1218. 最长定差子序列
    Java 1.0 到 Java 17历程
    java面试题:下面代码会创建多少个对象?
    Servlet 之 GenericServlet抽象类(适配器设计模式)的详解【底层源码分析】
    Mysql批量插入数据时如何解决重复问题
    快速搭建springcloud项目
    有关javascript中事件对象e
    杭电oj--求奇数的乘积
    评估指标及代码实现(NDCG)
    [附源码]java毕业设计法律咨询信息管理系统
  • 原文地址:https://blog.csdn.net/u011415782/article/details/133343314