Java中的微信支付(3):API V3對微信服務(wù)器響應(yīng)進(jìn)行簽名驗(yàn)證

1. 前言

牢記一句話:公鑰加密,私鑰解密泼疑;私鑰加簽控汉,公鑰驗(yàn)簽。

微信支付V3版本前兩篇分別講了如何對請求做簽名和如何獲取并刷新微信平臺(tái)公鑰敏晤,本篇將繼續(xù)展開如何對微信支付響應(yīng)結(jié)果的驗(yàn)簽。

2. 為什么要對響應(yīng)驗(yàn)簽

微信支付會(huì)在回調(diào)的HTTP頭部中包括回調(diào)報(bào)文的簽名鸡岗。商戶必須驗(yàn)證響應(yīng)的簽名鸳劳,保證響應(yīng)確實(shí)來自微信支付服務(wù)器,避免中間人攻擊宝鼓。而驗(yàn)證響應(yīng)簽名除了需要微信平臺(tái)的公鑰外還需要從請求頭的其它參數(shù)刑棵。

假設(shè)以下就是微信支付服務(wù)器的響應(yīng):

HTTP/1.1 200 OK
Server: nginx
Date: Tue, 02 Apr 2019 12:59:40 GMT
Content-Type: application/json; charset=utf-8
Content-Length: 2204
Connection: keep-alive
Keep-Alive: timeout=8
Content-Language: zh-CN
Request-ID: e2762b10-b6b9-5108-a42c-16fe2422fc8a
Wechatpay-Nonce: c5ac7061fccab6bf3e254dcf98995b8c
Wechatpay-Signature: CtcbzwtQjN8rnOXItEBJ5aQFSnIXESeV28Pr2YEmf9wsDQ8Nx25ytW6FXBCAFdrr0mgqngX3AD9gNzjnNHzSGTPBSsaEkIfhPF4b8YRRTpny88tNLyprXA0GU5ID3DkZHpjFkX1hAp/D0fva2GKjGRLtvYbtUk/OLYqFuzbjt3yOBzJSKQqJsvbXILffgAmX4pKql+Ln+6UPvSCeKwznvtPaEx+9nMBmKu7Wpbqm/+2ksc0XwjD+xlvlECkCxfD/OJ4gN3IurE0fpjxIkvHDiinQmk51BI7zQD8k1znU7r/spPqB+vZjc5ep6DC5wZUpFu5vJ8MoNKjCu8wnzyCFdA==
Wechatpay-Timestamp: 1554209980
Wechatpay-Serial: 5157F09EFDC096DE15EBE81A47057A7232F1B8E1
Cache-Control: no-cache, must-revalidate

{"prepay_id":"wx2922034726858082fbd40b511c67630000"}

檢查平臺(tái)證書序列號(hào)

微信支付響應(yīng)的時(shí)候會(huì)攜帶一個(gè)微信平臺(tái)證書序列號(hào),從響應(yīng)頭中的Wechatpay-Serial字段中獲取值愚铡,用來提示我們要使用該序列號(hào)的證書來進(jìn)行驗(yàn)簽蛉签,如果不存在就需要我們刷新證書,而上一文我們將平臺(tái)證書序列號(hào)和證書以鍵值對存在HashMap中沥寥,我們只需要檢查是否存在即可碍舍,不存在就刷新。

構(gòu)造驗(yàn)簽名串

從響應(yīng)結(jié)果中獲取對應(yīng)下面方法的三個(gè)參數(shù)就可以構(gòu)造出驗(yàn)簽名串营曼。

/**
 * 構(gòu)造驗(yàn)簽名串.
 *
 * @param wechatpayTimestamp HTTP頭 Wechatpay-Timestamp 中的應(yīng)答時(shí)間戳乒验。
 * @param wechatpayNonce     HTTP頭 Wechatpay-Nonce 中的應(yīng)答隨機(jī)串
 * @param body               響應(yīng)體
 * @return the string
 */
public String responseSign(String wechatpayTimestamp, String wechatpayNonce, String body) {
    return Stream.of(wechatpayTimestamp, wechatpayNonce, body)
            .collect(Collectors.joining("\n", "", "\n"));
}

驗(yàn)證簽名

待驗(yàn)證的簽名從響應(yīng)頭中的Wechatpay-Signature字段中獲取,我們使用微信支付平臺(tái)公鑰對驗(yàn)簽名串和簽名進(jìn)行SHA256 with RSA簽名驗(yàn)證蒂阱。

   // 構(gòu)造驗(yàn)簽名串  
        final String signatureStr = responseSign(wechatpayTimestamp, wechatpayNonce, body);
   // 加載SHA256withRSA簽名器
        Signature signer = Signature.getInstance("SHA256withRSA");
  // 用微信平臺(tái)公鑰對簽名器進(jìn)行初始化
        signer.initVerify(certificate);
   // 把我們構(gòu)造的驗(yàn)簽名串更新到簽名器中
        signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));
   // 把請求頭中微信服務(wù)器返回的簽名用Base64解碼 并使用簽名器進(jìn)行驗(yàn)證
        boolean result = signer.verify(Base64Utils.decodeFromString(wechatpaySignature));

完整的驗(yàn)簽代碼

/**
 * 我方對響應(yīng)驗(yàn)簽锻全,和應(yīng)答簽名做比較狂塘,使用微信平臺(tái)證書.
 *
 * @param wechatpaySerial    response.headers['Wechatpay-Serial']    當(dāng)前使用的微信平臺(tái)證書序列號(hào)
 * @param wechatpaySignature response.headers['Wechatpay-Signature']   微信平臺(tái)簽名
 * @param wechatpayTimestamp response.headers['Wechatpay-Timestamp']  微信服務(wù)器的時(shí)間戳
 * @param wechatpayNonce     response.headers['Wechatpay-Nonce']   微信服務(wù)器提供的隨機(jī)串
 * @param body               response.body 微信服務(wù)器的響應(yīng)體
 * @return the boolean
 */
@SneakyThrows
public boolean responseSignVerify(String wechatpaySerial, String wechatpaySignature, String wechatpayTimestamp, String wechatpayNonce, String body) {

    if (CERTIFICATE_MAP.isEmpty() || !CERTIFICATE_MAP.containsKey(wechatpaySerial)) {
        refreshCertificate();
    }
    Certificate certificate = CERTIFICATE_MAP.get(wechatpaySerial);

    final String signatureStr = createSign(wechatpayTimestamp, wechatpayNonce, body);
    Signature signer = Signature.getInstance("SHA256withRSA");
    signer.initVerify(certificate);
    signer.update(signatureStr.getBytes(StandardCharsets.UTF_8));

    return signer.verify(Base64Utils.decodeFromString(wechatpaySignature));
}

CERTIFICATE_MAP 平臺(tái)證書容器可參考上一篇文章。

3. 總結(jié)

驗(yàn)簽通過就說明我們請求的響應(yīng)來自微信服務(wù)器就可以針對結(jié)果進(jìn)行對應(yīng)的邏輯處理了鳄厌,微信支付API無論是V2還是V3都包含了使用Api證書對請求進(jìn)行加簽荞胡,對響應(yīng)結(jié)果進(jìn)行驗(yàn)簽的流程,十分考驗(yàn)對密碼摘要算法的使用了嚎,其它就是組織參數(shù)調(diào)用Http請求泪漂。如果你能夠掌握這一能力就會(huì)在面試中和工作中占到優(yōu)勢。好了今天分享就到這里歪泳,多多關(guān)注: 碼農(nóng)小胖哥 獲取更多實(shí)用的編程干貨萝勤。

關(guān)注公眾號(hào):碼農(nóng)小胖哥,獲取更多資訊

個(gè)人博客:https://felord.cn

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末呐伞,一起剝皮案震驚了整個(gè)濱河市敌卓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌伶氢,老刑警劉巖趟径,帶你破解...
    沈念sama閱讀 223,207評論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異癣防,居然都是意外死亡蜗巧,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評論 3 400
  • 文/潘曉璐 我一進(jìn)店門蕾盯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幕屹,“玉大人,你說我怎么就攤上這事刑枝∠闵ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 170,031評論 0 366
  • 文/不壞的土叔 我叫張陵装畅,是天一觀的道長靠娱。 經(jīng)常有香客問我,道長掠兄,這世上最難降的妖魔是什么像云? 我笑而不...
    開封第一講書人閱讀 60,334評論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮蚂夕,結(jié)果婚禮上迅诬,老公的妹妹穿的比我還像新娘。我一直安慰自己婿牍,他們只是感情好侈贷,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著等脂,像睡著了一般俏蛮。 火紅的嫁衣襯著肌膚如雪撑蚌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,895評論 1 314
  • 那天搏屑,我揣著相機(jī)與錄音争涌,去河邊找鬼。 笑死辣恋,一個(gè)胖子當(dāng)著我的面吹牛亮垫,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播伟骨,決...
    沈念sama閱讀 41,300評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼饮潦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了携狭?” 一聲冷哼從身側(cè)響起害晦,我...
    開封第一講書人閱讀 40,264評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎暑中,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲫剿,經(jīng)...
    沈念sama閱讀 46,784評論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鳄逾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了灵莲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片雕凹。...
    茶點(diǎn)故事閱讀 40,989評論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖政冻,靈堂內(nèi)的尸體忽然破棺而出枚抵,到底是詐尸還是另有隱情,我是刑警寧澤明场,帶...
    沈念sama閱讀 36,649評論 5 351
  • 正文 年R本政府宣布汽摹,位于F島的核電站,受9級(jí)特大地震影響苦锨,放射性物質(zhì)發(fā)生泄漏逼泣。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評論 3 336
  • 文/蒙蒙 一舟舒、第九天 我趴在偏房一處隱蔽的房頂上張望拉庶。 院中可真熱鬧,春花似錦秃励、人聲如沸氏仗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽皆尔。三九已至呐舔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間床佳,已是汗流浹背滋早。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評論 1 275
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留砌们,地道東北人杆麸。 一個(gè)月前我還...
    沈念sama閱讀 49,452評論 3 379
  • 正文 我出身青樓,卻偏偏與公主長得像浪感,于是被迫代替她去往敵國和親昔头。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評論 2 361