首发php版支付宝公钥证书

从java代码中搬过来的,对官方的文档很无语,坑太多了
官方文档[https://docs.open.alipay.com/291/106118]里面是这样写的:

说明:
公钥证书方式下,开发者发送给开放平台网关请求参数中,需携带应用公钥证书SN(app_cert_sn)、支付宝根证书SN(alipay_root_cert_sn),若不携带这两个参数,网关会拒绝请求。SN值是通过解析X.509证书文件中签发机构名称(name)以及内置序列号(serialNumber),将二者拼接后的字符串计算MD5值获取,可参考开放平台SDK源码中AlipaySignature.getCertSN实现app_cert_sn的提取

注意点:

  1. 解析X.509证书文件中签发机构名称是什么玩意,php解析出来的是个数组!到底哪个是的,是什么格式的?
  2. 拼接MD5,不读java代码,打死也猜不出来支付宝根证书和应用证书的方法是不一样的
    php解析出来的

PHP生成SN源码

应用证书解析格式如下

2.png

需要拼接成:CN=Ant Financial Certification Authority Class 2 R1,OU=Certification Authority,O=Ant Financial,C=CN

支付宝根证书

要拆解成多个证书,分别解析并生成MD5,最后用"_"拼接
注意:serialNumber序列号可能会出现0x开头的,需要转换一下

代码


    /**
     * 生成应用证书SN
     * @param $certPath
     * @return string
     * @throws /Exception
     */
    public static function getCertSN($certPath): string
    {
        if (!is_file($certPath)) {
            throw new \Exception('unknown certPath -- [getCertSN]');
        }
        $x509data = file_get_contents($certPath);
        if ($x509data === false) {
            throw new \Exception('Alipay CertSN Error -- [getCertSN]');
        }
        openssl_x509_read($x509data);
        $certdata = openssl_x509_parse($x509data);
        if (empty($certdata)) {
            throw new \Exception('Alipay openssl_x509_parse Error -- [getCertSN]');
        }
        $issuer_arr = [];
        foreach ($certdata['issuer'] as $key => $val) {
            $issuer_arr[] = $key . '=' . $val;
        }
        $issuer = implode(',', array_reverse($issuer_arr));
        Log::debug('getCertSN:', [$certPath, $issuer, $certdata['serialNumber']]);
        return md5($issuer . $certdata['serialNumber']);
    }

    /**
     * 0x转高精度数字
     * @param $hex
     * @return int|string
     */
    private static function bchexdec($hex)
    {
        $dec = 0;
        $len = strlen($hex);
        for ($i = 1; $i <= $len; $i++) {
            $dec = bcadd($dec, bcmul(strval(hexdec($hex[$i - 1])), bcpow('16', strval($len - $i))));
        }
        return $dec;
    }

    /**
     * 生成支付宝根证书SN
     * @param $certPath
     * @return string
     * @throws /Exception
     */
    public static function getRootCertSN($certPath)
    {
        if (!is_file($certPath)) {
            throw new \Exception('unknown certPath -- [getRootCertSN]');
        }
        $x509data = file_get_contents($certPath);
        if ($x509data === false) {
            throw new \Exception('Alipay CertSN Error -- [getRootCertSN]');
        }
        $kCertificateEnd = "-----END CERTIFICATE-----";
        $certStrList = explode($kCertificateEnd, $x509data);
        $md5_arr = [];
        foreach ($certStrList as $one) {
            if (!empty(trim($one))) {
                $_x509data = $one . $kCertificateEnd;
                openssl_x509_read($_x509data);
                $_certdata = openssl_x509_parse($_x509data);
                if (in_array($_certdata['signatureTypeSN'], ['RSA-SHA256', 'RSA-SHA1'])) {
                    $issuer_arr = [];
                    foreach ($_certdata['issuer'] as $key => $val) {
                        $issuer_arr[] = $key . '=' . $val;
                    }
                    $_issuer = implode(',', array_reverse($issuer_arr));
                    if (strpos($_certdata['serialNumber'], '0x') === 0) {
                        $serialNumber = self::bchexdec($_certdata['serialNumber']);
                    } else {
                        $serialNumber = $_certdata['serialNumber'];
                    }
                    $md5_arr[] = md5($_issuer . $serialNumber);
                    Log::debug('getRootCertSN Sub:', [$certPath, $_issuer, $serialNumber]);
                }
            }
        }
        return implode('_', $md5_arr);
    }
最后修改:2019 年 09 月 18 日
如果觉得我的文章对你有用,请随意赞赏