您好,登錄后才能下訂單哦!
本篇內容主要講解“Python解析支付寶公鑰證書的方法”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Python解析支付寶公鑰證書的方法”吧!
由于工作需要,我們開發的 App 需要接入支付寶的支付功能。于是我們著手了解 Alipay 相關的 api 文檔。
經過斟酌后,我們選擇使用了一個第三方 SDK (https://github.com/fzlee/alipay)。這里不得不吐槽一下官方的python SDK, 從包路徑到使用,都帶有強烈的 java 風格,沒有了 python 的簡約氣息。
盡管使用了 SDK,但是我們發現,最新的支付寶都使用了公鑰證書來簽名。而官方 SDK 也只有 java 才支持公鑰證書方式,我們使用的第三方 SDK 也沒有。 于是乎我們不得不自己來實現公鑰證書的解析,但是網絡上關于自行實現簽名的內容比較少,僅有提供一些說明。
官方提供了自行實現簽名的過程 https://docs.open.alipay.com/291/106118。
其中比較關鍵的是從證書提取app_cert_sn
和 alipay_root_cert_sn
兩個關鍵參數,這里給出 Java 中的實現:
/** * 從公鑰證書中提取公鑰序列號 * * @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); }
這里和官網所說的流程大概相同:
解析X.509證書文件,獲取證書簽發機構名稱(name)以及證書內置序列號(serialNumber)。
將name與serialNumber拼接成字符串,再對該字符串做MD5計算。
第一步
中解析X.509證書比較容易,在 python 實現中我們使用了 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:
以上是我對支付寶公鑰證書驗證的大致理解,最后的算法類型也是我的猜測,有問題可以告訴我哦。這么看起來不是特別困難的問題,但是在解決問題的過程中的確花了很多時間,網絡上能提供的資料也只有支付寶官網的文檔和上面 的一個php實現的博客。解決完讓我豁然開朗,也希望還在炮坑的同學能從中受益。
目前我是在alipay ( 我覺得做的還可以 )基礎上加入了證書簽名。需要的朋友可以直接下載使用該SDK。
到此,相信大家對“Python解析支付寶公鑰證書的方法”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。