溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Python解析支付寶公鑰證書的方法

發(fā)布時間:2021-06-25 10:36:12 來源:億速云 閱讀:431 作者:chen 欄目:大數(shù)據(jù)

本篇內容主要講解“Python解析支付寶公鑰證書的方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Python解析支付寶公鑰證書的方法”吧!

由于工作需要,我們開發(fā)的 App 需要接入支付寶的支付功能。于是我們著手了解 Alipay 相關的 api 文檔。
經過斟酌后,我們選擇使用了一個第三方 SDK (https://github.com/fzlee/alipay)。這里不得不吐槽一下官方的python SDK, 從包路徑到使用,都帶有強烈的 java 風格,沒有了 python 的簡約氣息。
盡管使用了 SDK,但是我們發(fā)現(xiàn),最新的支付寶都使用了公鑰證書來簽名。而官方 SDK 也只有 java 才支持公鑰證書方式,我們使用的第三方 SDK 也沒有。 于是乎我們不得不自己來實現(xiàn)公鑰證書的解析,但是網(wǎng)絡上關于自行實現(xiàn)簽名的內容比較少,僅有提供一些說明。
官方提供了自行實現(xiàn)簽名的過程 https://docs.open.alipay.com/291/106118。
其中比較關鍵的是從證書提取app_cert_snalipay_root_cert_sn兩個關鍵參數(shù),這里給出 Java 中的實現(xiàn):

/**
 * 從公鑰證書中提取公鑰序列號
 *
 * @param certPath 公鑰證書存放路徑,例如:/home/admin/cert.crt
 * @return 公鑰證書序列號
 * @throws AlipayApiException
 */
public static String getCertSN(String certPath) throws AlipayApiException {
    InputStream inputStream = null;
    try {
        inputStream = new FileInputStream(certPath);
        CertificateFactory cf = CertificateFactory.getInstance("X.509");
        X509Certificate cert = (X509Certificate) cf.generateCertificate(inputStream);
        MessageDigest md = MessageDigest.getInstance("MD5");
        md.update((cert.getIssuerX500Principal().getName() + cert.getSerialNumber()).getBytes());
        String certSN = new BigInteger(1, md.digest()).toString(16);
        //BigInteger會把0省略掉,需補全至32位
        certSN = fillMD5(certSN);
        return certSN;

    } catch (NoSuchAlgorithmException e) {
        throw new AlipayApiException(e);
    } catch (IOException e) {
        throw new AlipayApiException(e);
    } catch (CertificateException e) {
        throw new AlipayApiException(e);
    } finally {
        try {
            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            throw new AlipayApiException(e);
        }
    }
}

/**
 * 獲取根證書序列號
 *
 * @param rootCertContent
 * @return
 */
public static String getRootCertSN(String rootCertContent) {
    String rootCertSN = null;
    try {
        X509Certificate[] x509Certificates = readPemCertChain(rootCertContent);
        MessageDigest md = MessageDigest.getInstance("MD5");
        for (X509Certificate c : x509Certificates) {
            if (c.getSigAlgOID().startsWith("1.2.840.113549.1.1")) {
                md.update((c.getIssuerX500Principal().getName() + c.getSerialNumber()).getBytes());
                String certSN = new BigInteger(1, md.digest()).toString(16);
                //BigInteger會把0省略掉,需補全至32位
                certSN = fillMD5(certSN);
                if (StringUtils.isEmpty(rootCertSN)) {
                    rootCertSN = certSN;
                } else {
                    rootCertSN = rootCertSN + "_" + certSN;
                }
            }

        }
    } catch (Exception e) {
        AlipayLogger.logBizError(("提取根證書失敗"));
    }
    return rootCertSN;

}

private static String fillMD5(String md5) {
    return md5.length() == 32 ? md5 : fillMD5("0" + md5);
}

這里和官網(wǎng)所說的流程大概相同:

  • 解析X.509證書文件,獲取證書簽發(fā)機構名稱(name)以及證書內置序列號(serialNumber)。

  • 將name與serialNumber拼接成字符串,再對該字符串做MD5計算。

第一步中解析X.509證書比較容易,在 python 實現(xiàn)中我們使用了 openssl 來解析證書:

cert = OpenSSL.crypto.load_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)

但是在獲取 name 和 serialNumber 碰到了障礙。 在 Java 中我們看到 md.update((c.getIssuerX500Principal().getName() + c.getSerialNumber()).getBytes()); 這一行可以輕松提取 name 和 serialName。 可惜的是,openssl 里只有get_serial_number這樣的 API 來提取序列號,沒有像 java 里 getIssuerX500Principal 來獲取他想要的機構名稱。 經過長時間的資料查詢和研究,從 https://sbing.vip/archives/2019-new-alipay-php-docking.html 這里找到了線索:

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

于是找到了解決方法:name = 'CN={},OU={},O={},C={}'.format(certIssue.CN, certIssue.OU, certIssue.O, certIssue.C)
第二步中的拼接和 MD5 校驗就比較簡單,使用 python 自帶的 hashlib 就可以完成,并且比 Java 更簡潔。
最后一個問題來自于根證書,源碼顯示根證書包含多個證書信息,讀取文件的時候需要使用 split('\n\n') 來獲取證書字符串列表,再遍歷獲取證書 SN 信息。 還有源碼里做了篩選if (c.getSigAlgOID().startsWith("1.2.840.113549.1.1")),Openssl里也沒有這樣的 API 可以調度。 我沒有選擇像它那樣解析出算法的 OID。我猜想這個就是為了找到指定算法類型,于是我使用了別的方法代替:

try:
    sigAlg = cert.get_signature_algorithm()
except ValueError:
    continue
if b'rsaEncryption' in sigAlg or b'RSAEncryption' in sigAlg:

以上是我對支付寶公鑰證書驗證的大致理解,最后的算法類型也是我的猜測,有問題可以告訴我哦。這么看起來不是特別困難的問題,但是在解決問題的過程中的確花了很多時間,網(wǎng)絡上能提供的資料也只有支付寶官網(wǎng)的文檔和上面 的一個php實現(xiàn)的博客。解決完讓我豁然開朗,也希望還在炮坑的同學能從中受益。
目前我是在alipay ( 我覺得做的還可以 )基礎上加入了證書簽名。需要的朋友可以直接下載使用該SDK。

到此,相信大家對“Python解析支付寶公鑰證書的方法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續(xù)學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。

AI