# 概述
在微信支付API v3的所有請(qǐng)求應(yīng)答場(chǎng)景,開(kāi)發(fā)者都需要進(jìn)行簽名驗(yàn)簽。如果你是第一次接入微信支付API v3接口,請(qǐng)仔細(xì)閱讀以下系列文檔,并跟著示例操作一次,這將有效幫忙你理解簽名驗(yàn)簽機(jī)制,解決簽名驗(yàn)簽報(bào)錯(cuò)。
1、所有請(qǐng)求商戶都需要使用【商戶API證書(shū)私鑰】對(duì)請(qǐng)求進(jìn)行簽名,微信支付會(huì)在收到請(qǐng)求后使用【商戶API證書(shū)公鑰】進(jìn)行簽名的驗(yàn)證。如果簽名驗(yàn)證不通過(guò),微信支付API v3將會(huì)拒絕處理請(qǐng)求,并返回401 Unauthorized。
2、所有應(yīng)答,微信支付都會(huì)使用平臺(tái)證書(shū)私鑰簽名,商戶需要使用平臺(tái)證書(shū)公鑰驗(yàn)證簽名.(文件下載接口和首次下載平臺(tái)證書(shū)除外)
本文介紹如何驗(yàn)證簽名,計(jì)算簽名部分文檔說(shuō)明請(qǐng)參考操作指引-如何生成請(qǐng)求簽名 (opens new window)。
1、微信支付在 API 應(yīng)答的 HTTP 頭部 Wechatpay-Signature
中提供了應(yīng)答簽名。商戶應(yīng)驗(yàn)證應(yīng)答簽名,以確保數(shù)據(jù)來(lái)自微信支付且未經(jīng)第三方篡改。
2、在通知回調(diào)的 HTTP 頭部中,微信支付會(huì)包含回調(diào)報(bào)文的簽名。商戶必須驗(yàn)證回調(diào)的簽名,以確保回調(diào)由微信支付發(fā)送。
請(qǐng)參考以下內(nèi)容,自行驗(yàn)證簽名,并遵循指引防止重放攻擊,以及應(yīng)對(duì)簽名探測(cè)流量。
驗(yàn)證簽名分為獲取平臺(tái)證書(shū)、構(gòu)造驗(yàn)簽串、獲取應(yīng)答簽名、驗(yàn)證簽名共4步
# 1、獲取平臺(tái)證書(shū)
# 1.1 首次下載證書(shū)
第一次獲取平臺(tái)證書(shū)需要通過(guò)證書(shū)工具,請(qǐng)按照以下3個(gè)步驟操作
(1)點(diǎn)擊這里 (opens new window)下載jar包
(2)執(zhí)行以下命令
1java -jar CertificateDownloader.jar -k ${apiV3key} -m ${mchId} -f ${mchPrivateKeyFilePath} -s ${mchSerialNo} -o ${outputFilePath}
你會(huì)得到平臺(tái)證書(shū),文件名類似于wechatpay_123456777B4A9CC78902B44B65E04B9751CE12.pem
。
參數(shù)說(shuō)明
- apiV3key:是指APIv3密鑰,在賬號(hào)中心->API安全->APIv3密鑰中設(shè)置,如何獲取APIv3密鑰具體操作參考這里 (opens new window)
- mchId:是指商戶號(hào)
- mchPrivateKeyFilePath:商戶API證書(shū)私鑰存放的路徑,你需要獲取商戶API證書(shū)私鑰(apiclient_key.pem),并保存到某個(gè)路徑。如何獲取商戶API證書(shū)私鑰具體操作參考這里 (opens new window)
- mchSerialNo:商戶API證書(shū)的序列號(hào),如何查看序列號(hào)請(qǐng)參考證書(shū)相關(guān)問(wèn)題 (opens new window)
- outputFilePath:平臺(tái)證書(shū)保存的路徑,開(kāi)發(fā)者自定義
(3)驗(yàn)證證書(shū)有效性
第一次下載證書(shū)后,我們強(qiáng)烈建議參考如何通過(guò)證書(shū)信任鏈驗(yàn)證平臺(tái)證書(shū) (opens new window),驗(yàn)證證書(shū)的真實(shí)性。
# 1.2 二次獲取平臺(tái)證書(shū)
除了首次下載證書(shū)需要通過(guò)以上方式,后續(xù)下載證書(shū)請(qǐng)使用微信支付平臺(tái)證書(shū)下載接口
# 2. 構(gòu)造驗(yàn)簽名串
# 2.1 構(gòu)造驗(yàn)簽串前的檢查
- 您應(yīng)先檢查 HTTP 頭 Wechatpay-Serial 的內(nèi)容是否跟商戶當(dāng)前所持有的微信支付平臺(tái)證書(shū)的序列號(hào)一致。若不一致,請(qǐng)重新獲取證書(shū)。否則,簽名的私鑰和證書(shū)不匹配,將驗(yàn)證失敗。
- 重放攻擊 (opens new window)是指攻擊者截取報(bào)文及其簽名,并以惡意或欺詐目的重新傳輸數(shù)據(jù)的一種攻擊手段。為了降低此類攻擊的風(fēng)險(xiǎn),微信支付在 HTTP 頭
Wechatpay-Timestamp
中提供了生成簽名的時(shí)間戳。若微信支付需要重新發(fā)送某個(gè)通知回調(diào),我們也會(huì)重新生成相應(yīng)的時(shí)間戳和簽名。在驗(yàn)證簽名之前,商戶系統(tǒng)應(yīng)檢查時(shí)間戳是否已過(guò)期。我們建議商戶系統(tǒng)允許最多5分鐘的時(shí)間偏差。如果時(shí)間戳與當(dāng)前時(shí)間的偏差超過(guò)5分鐘,您應(yīng)拒絕處理當(dāng)前的響應(yīng)或回調(diào)通知。
提示
您應(yīng)當(dāng)采用網(wǎng)絡(luò)時(shí)間協(xié)議(NTP)等機(jī)制實(shí)現(xiàn)商戶系統(tǒng)的時(shí)鐘同步,保證時(shí)間準(zhǔn)確。
# 2.2開(kāi)始構(gòu)造驗(yàn)簽串
以某次native下單接口的應(yīng)答為例
1Server: nginx2Date: Mon, 05 Aug 2024 09:33:41 GMT3Content-Type: application/json; charset=utf-84Content-Length: 525Connection: keep-alive6Keep-Alive: timeout=87Cache-Control: no-cache, must-revalidate8X-Content-Type-Options: nosniff9Request-ID: 08F5B8C2B506102C18FDDFEEA30620BE821E28EDC405-010Content-Language: zh-CN11Wechatpay-Nonce: d824f2e086d3c1df967785d13fcd22ef12Wechatpay-Signature: mfI1CPqvBrgcXfgXMFjdNIhBf27ACE2YyeWsWV9ZI7T7RU0vHvbQpu9Z32ogzc+k8ZC5n3kz7h70eWKjgqNdKQF0eRp8mVKlmfzMLBVHbssB9jEZEDXThOX1XFqX7s7ymia1hoHQxQagPGzkdWxtlZPZ4ZPvr1RiqkgAu6Is8MZgXXrRoBKqjmSdrP1N7uxzJ/cjfSiis9FiLjuADoqmQ1P7p2N876YPAol7Rn0+GswwAwxldbdLrmVSjfytfSBJFqTMHn4itojgxSWWN1byuckQt8hSTEv/Lg97QoeGniYP17T80pJeQyL3b+295FPHSO2AtvCgyIbKMZ0BALilAA==13Wechatpay-Timestamp: 172285042114Wechatpay-Serial: 4DF076AC5A7D968D4A8B0B9C599A74CB4CF8EE8A15Wechatpay-Signature-Type: WECHATPAY2-SHA256-RSA20481617{"code_url":"weixin://wxpay/bizpayurl?pr=JyC91EIz1"}
(1)請(qǐng)從應(yīng)答或通知回調(diào)中獲取以下信息:
- HTTP 頭
Wechatpay-Timestamp
中的應(yīng)答時(shí)間戳 - HTTP 頭
Wechatpay-Nonce
中的應(yīng)答隨機(jī)串 - 應(yīng)答報(bào)文主體(Response Body),請(qǐng)使用原始報(bào)文主體執(zhí)行驗(yàn)簽。如果您使用了某個(gè)框架,要確保它不會(huì)篡改報(bào)文主體。對(duì)報(bào)文主體的任何篡改都會(huì)導(dǎo)致驗(yàn)證失敗。
(2)然后,請(qǐng)按照以下規(guī)則構(gòu)造應(yīng)答的驗(yàn)簽名串。簽名串共有三行,行尾以\n
結(jié)束,包括最后一行。\n
為換行符(ASCII 編碼值為 0x0A)。若應(yīng)答報(bào)文主體為空(如 HTTP 狀態(tài)碼為204 No Content
),最后一行僅為一個(gè)\n
換行符。
1應(yīng)答時(shí)間戳\n2應(yīng)答隨機(jī)串\n3應(yīng)答報(bào)文主體\n
則驗(yàn)簽名串為
11722850421\n2d824f2e086d3c1df967785d13fcd22ef\n3{"code_url":"weixin://wxpay/bizpayurl?pr=JyC91EIz1"}\n
# 3.獲取應(yīng)答簽名
微信支付的應(yīng)答簽名通過(guò) HTTP 頭Wechatpay-Signature
傳遞。(請(qǐng)注意,示例可能存在換行,實(shí)際數(shù)據(jù)應(yīng)在一行)
1Wechatpay-Signature: mfI1CPqvBrgcXfgXMFjdNIhBf27ACE2YyeWsWV9ZI7T7RU0vHvbQpu9Z32ogzc+k8ZC5n3kz7h70eWKjgqNdKQF0eRp8mVKlmfzMLBVHbssB9jEZEDXThOX1XFqX7s7ymia1hoHQxQagPGzkdWxtlZPZ4ZPvr1RiqkgAu6Is8MZgXXrRoBKqjmSdrP1N7uxzJ/cjfSiis9FiLjuADoqmQ1P7p2N876YPAol7Rn0+GswwAwxldbdLrmVSjfytfSBJFqTMHn4itojgxSWWN1byuckQt8hSTEv/Lg97QoeGniYP17T80pJeQyL3b+295FPHSO2AtvCgyIbKMZ0BALilAA==
使用 base64 解碼Wechatpay-Signature
字段值,得到應(yīng)答簽名。
提示
某些代理服務(wù)器或 CDN 服務(wù)提供商,在轉(zhuǎn)發(fā)時(shí)可能會(huì)“過(guò)濾”微信支付擴(kuò)展的 HTTP 頭,導(dǎo)致應(yīng)用層無(wú)法獲取微信支付的簽名信息。商戶遇到這種情況時(shí),我們建議嘗試調(diào)整代理服務(wù)器配置,或者通過(guò)直連的方式訪問(wèn)微信支付的服務(wù)器和接收通知回調(diào)。
# 4.驗(yàn)證簽名
很多編程語(yǔ)言的簽名驗(yàn)證函數(shù)支持對(duì)驗(yàn)簽名串和簽名進(jìn)行簽名驗(yàn)證。強(qiáng)烈建議商戶調(diào)用該類函數(shù),使用微信支付平臺(tái)公鑰對(duì)驗(yàn)簽名串和簽名進(jìn)行 SHA256 with RSA 簽名驗(yàn)證。
下面展示使用命令行演示如何進(jìn)行驗(yàn)簽。這里已經(jīng)獲取了平臺(tái)證書(shū)并保存為1900009191_wxp_cert.pem
,你可以直接將以下平臺(tái)證書(shū)公鑰保存到本地用于操作驗(yàn)證。
1、首先,從微信支付平臺(tái)證書(shū)導(dǎo)出微信支付平臺(tái)公鑰。
1$ openssl x509 -in 1900009191_wxp_cert.pem -pubkey -noout > 1900009191_wxp_pub.pem2$ cat 1900009191_wxp_pub.pem3-----BEGIN PUBLIC KEY-----4MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwXNI6sdlknHBnK8Fu2U65Cwor9qY747jP8KAfeBMeveEt1TqaHkLfaSD07trZLhGpfs8/AHqjhgSMO1O10YQW6OrrJ4hjIWPKqxbgrYMkBQc+mwdiWp4W3ByCqxBRagCveCXRWCmuJYovl9H/bsDI07iGbpVtEOghJtfciisYSgxcLufUDTRkvwxjIBK1pCRjk33jJ5YTBWTHMRtMAOcFLN8F6hdEYdX8SPsgHHeLZ5Lv2T/686w1xtgCHef/sd4uSfWmyzsalQdHG/e4IyYmrhx9+O3VBoNDzE3nx23bFeV/RVNCG7cV6VhmYokJNHa/erIPkEmEFID6A5wQOXuxUkmJ10WwIDAQAB11-----END PUBLIC KEY-----
提示
Java支持使用證書(shū)初始化簽名對(duì)象,詳見(jiàn)initVerify(Certificate) (opens new window),并不需要先導(dǎo)出公鑰。
2、然后,使用 base64 解碼應(yīng)答簽名,將保存為文件 signature.txt
。(以下命令行均在一行)
1openssl base64 -d <<< 'mfI1CPqvBrgcXfgXMFjdNIhBf27ACE2YyeWsWV9ZI7T7RU0vHvbQpu9Z32ogzc+k8ZC5n3kz7h70eWKjgqNdKQF0eRp8mVKlmfzMLBVHbssB9jEZEDXThOX1XFqX7s7ymia1hoHQxQagPGzkdWxtlZPZ4ZPvr1RiqkgAu6Is8MZgXXrRoBKqjmSdrP1N7uxzJ/cjfSiis9FiLjuADoqmQ1P7p2N876YPAol7Rn0+GswwAwxldbdLrmVSjfytfSBJFqTMHn4itojgxSWWN1byuckQt8hSTEv/Lg97QoeGniYP17T80pJeQyL3b+295FPHSO2AtvCgyIbKMZ0BALilAA==' > signature.txt
3、最后,驗(yàn)證簽名,得到驗(yàn)簽結(jié)果,請(qǐng)確認(rèn)你的結(jié)果和文檔的結(jié)果一致,如果驗(yàn)簽結(jié)果是Verification Failure,請(qǐng)確認(rèn)是否獲取到了正確的平臺(tái)證書(shū)的公鑰或者驗(yàn)簽串是否有嚴(yán)格按照文檔格式換行
1$ openssl dgst -sha256 -verify 1900009191_wxp_pub.pem -signature signature.txt << EOF217228504213d824f2e086d3c1df967785d13fcd22ef4{"code_url":"weixin://wxpay/bizpayurl?pr=JyC91EIz1"}5EOF
1Verified OK
# 5. 應(yīng)對(duì)簽名探測(cè)流量
為了確保商戶系統(tǒng)的安全,微信支付會(huì)在極少數(shù)應(yīng)答或通知回調(diào)中生成錯(cuò)誤簽名,以探測(cè)商戶系統(tǒng)是否正確地驗(yàn)證了簽名。
商戶系統(tǒng)不應(yīng)對(duì)探測(cè)流量進(jìn)行特殊處理,而應(yīng)將其視為正常的應(yīng)答或通知回調(diào),并對(duì)其簽名進(jìn)行驗(yàn)證。
在排查問(wèn)題時(shí),您可以通過(guò)查看簽名值中的 WECHATPAY/SIGNTEST/
前綴快速判斷是否為探測(cè)流量。所有用于探測(cè)目的的簽名值都會(huì)包含此前綴。
在驗(yàn)簽失敗的情況下,我們建議商戶系統(tǒng)采取以下措施:
- 如果應(yīng)答的簽名驗(yàn)證失敗,商戶系統(tǒng)應(yīng)舍棄該應(yīng)答。為了提高用戶體驗(yàn),商戶系統(tǒng)可以適當(dāng)?shù)刂卦嚕蛘咦層脩糁匦掳l(fā)起請(qǐng)求。微信支付不會(huì)針對(duì)重試請(qǐng)求發(fā)起探測(cè)。
- 若通知回調(diào)的簽名驗(yàn)證失敗,商戶系統(tǒng)應(yīng)返回失敗(即應(yīng)答
4xx
或5xx
的狀態(tài)碼),等待微信支付攜帶正確簽名重新發(fā)送通知回調(diào)。
如果你有關(guān)于簽名探測(cè)任何疑問(wèn),請(qǐng)通過(guò)在線技術(shù)咨詢 (opens new window)聯(lián)系我們的技術(shù)支持。