支付寶介面的數字簽名

真正的飛魚發表於2023-01-26

簽名

某些情況下(例如使用者掃碼支付成功時),支付寶會給商戶系統傳送非同步通知。在傳送非同步通知時,支付寶會對通知引數進行簽名,並將 “簽名字串 sign” 作為通知引數傳送給商戶系統。支付寶簽名的步驟是:

  1. 拼接 “待簽名字串”;
  2. 呼叫簽名方法 sign();
  3. 拼接完整的請求 URL。

技術是為瞭解決問題而生的,進行數字簽名的目的是:

  • 確保資訊是由簽名者傳送的。防止有人冒充支付寶向商戶系統傳送非同步通知;
  • 確保資訊自簽名後 到 收到為止,資訊未被修改過。防止有人篡改請求引數的值。

如果商家設定的通知地址為 https://api.xx.com/receive_notify.htm,對應收到的通知示例如下:

https://api.xx.com/receive_notify.htm?

total_amount=2.00&buyer_id=2088102116773037&body=大樂透2.1&trade_no=2016071921001003030200089909&refund_fee=0.00&notify_time=2016-07-19 14:10:49&subject=大樂透2.1&sign_type=RSA2&charset=utf-8&notify_type=trade_status_sync&out_trade_no=0719141034-6418&gmt_close=2016-07-19 14:10:46&gmt_payment=2016-07-19 14:10:47&trade_status=TRADE_SUCCESS&version=1.0&

sign=kPbQIjX+xQc8F0/A6/AocELIjhhZnGbcBN6G4MM/HmfWL4ZiHM6fWl5NQhzXJusaklZ1LFuMo+lHQUELAYeugH8LYFvxnNajOvZhuxNFbN2LhF0l/KL8ANtj8oyPM4NN7Qft2kWJTDJUpQOzCzNnV9hDxh5AaT9FPqRS6ZKxnzM=&

gmt_create=2016-07-19 14:10:44&app_id=2015102700040153&seller_id=2088102119685838&notify_id=4a91b7a78a503640467525113fb7d8bg8e

1 、拼接 “待簽名字串”

支付寶對要傳送的通知引數進行簽名的第一步是:拼接 “待簽名字串”。

  1. 對需要簽名的引數進行字典排序:字典排序是按照引數的第一個字元的 ASCII 碼遞增排序(字母升序排序)。如果引數的第一個字元相同,則按照引數的第二個字元的 ASCII 碼遞增排序,以此類推。
  2. 對排序後的引數進行拼接,得到 “待簽名字串”:將排序後的引數與其對應值,組合成引數=引數值的格式,引數與引數之間用 & 字元連線起來,此時生成的字串為 “待簽名字串”。

2、呼叫簽名方法 sign()

支付寶對要傳送的通知引數進行簽名的第二步是:呼叫簽名演演算法對應的簽名方法 sign(),生成簽名字串 sign。

呼叫簽名演演算法對應的簽名方法時,需要提供以下幾個引數:待簽名字串 content、字元編碼方案 charset(UTF-8、GBK)和 簽名者的私鑰 privateKey。簽名方法 sign() 的處理邏輯如下:

  1. 對 “待簽名字串” 進行編碼,得到位元組陣列:使用指定的字元編碼方案,將 “待簽名字串” 編碼為位元組陣列(byte 型別的陣列)
  2. 進行簽名、Base64 編碼,得到簽名字串 sign:使用簽名者的私鑰 privateKey 對 “待簽名字串” 對應的位元組陣列進行簽名,並對簽名結果進行 Base64 編碼,以便在網路上傳輸。經過 Base64 編碼後的字串即為 “簽名字串 sign”。String sign = new String(Base64.encodeBase64(signed));

3、拼接完整的請求 URL

支付寶對要傳送的通知引數進行簽名的第三步是:拼接完整的請求 URL。

  1. 拼接 sign 引數:將生成的簽名字串作為 sign 引數的 value 拼接到請求資料中。
  2. Encode 請求資料:對所有一級 key 的 value 值進行 UrlEncode 編碼。
  3. 拼接完整的請求 URL:將編碼後的請求資料傳送至商戶系統指定的 URL。

驗籤

驗籤的全稱是:驗證指定內容的簽名是否正確。

商戶系統收到支付寶傳送的非同步通知後,商戶系統需要對支付寶傳送的通知引數進行驗籤處理。商戶系統驗籤的步驟是:

  1. 拼接 “待驗簽字串”;
  2. 獲取簽名字串 sign;
  3. 呼叫驗籤方法 verify(),獲得驗簽結果。

1、拼接 “待驗簽字串”

對支付寶傳送的通知引數進行驗籤的第一步是:拼接 “待驗簽字串”。

  1. 獲取簽名的引數:在通知返回的引數列表中,除去 sign、sign_type 兩個引數外,凡是通知返回的引數都是簽名的引數。
  2. 對簽名的引數進行 UrlDecode 解碼
  3. 對簽名的引數進行字典排序:字典排序是按照引數的第一個字元的 ASCII 碼遞增排序(字母升序排序)。如果引數的第一個字元相同,則按照引數的第二個字元的 ASCII 碼遞增排序,以此類推。
  4. 對排序後的引數進行拼接,得到 “待驗簽字串”:將排序後的引數與其對應值,組合成引數=引數值的格式,引數與引數之間用 & 字元連線起來,此時生成的字串為 “待驗簽字串”。
body=大樂透2.1&buyer_id=2088102116773037&charset=utf-8&gmt_close=2016-07-19 14:10:46&gmt_payment=2016-07-19 14:10:47&notify_time=2016-07-19 14:10:49&notify_type=trade_status_sync&out_trade_no=0719141034-6418&refund_fee=0.00&subject=大樂透2.1&total_amount=2.00&trade_no=2016071921001003030200089909&trade_status=TRADE_SUCCESS&version=1.0

2、取出簽名字串 sign

對支付寶傳送的通知引數進行驗籤的第二步是:從通知返回的引數列表中,取出簽名字串 sign。

3、呼叫驗籤方法 verify()

對支付寶傳送的通知引數進行驗籤的第三步是:呼叫簽名演演算法對應的驗籤方法 verify(),根據驗籤方法 verify() 的返回結果判定是否驗籤透過。


呼叫簽名演演算法對應的驗籤方法時,需要提供以下幾個引數:待驗簽字串 content、字元編碼方案 charset、簽名字串 sign 和 簽名者的公鑰 publicKey。驗籤方法 verify() 的處理邏輯如下:

  1. 對 “待驗簽字串” 進行編碼,得到位元組陣列:使用指定的字元編碼方案,將 “待驗籤符串” 編碼為位元組陣列(byte 型別的陣列)
  2. 對 “簽名字串 sign” 進行 Base64 解碼,得到位元組陣列:得到的這個位元組陣列即為當時簽名之後的初始位元組陣列(密文)。
  3. 使用公鑰驗證簽名boolean varifyResult = boosignature.verify(Base64.decodeBase64(sign.getBytes()))
    1. 使用簽名者的公鑰 publicKey 對 密文 進行 解密,得到 “支付寶通知的摘要”;
    2. 對 “待驗籤符串” 對應的位元組陣列做摘要,得到摘要結果;
    3. 將 “商戶計算出的摘要結果” 和 “解密得到的摘要” 作比較:如果二者相同,則說明驗籤成功;否則說明驗籤失敗。

驗籤成功後,商戶系統根據通知引數進行業務處理。

參考資料

自行實現簽名 - 支付寶檔案中心 (alipay.com)

自行實現驗籤 - 支付寶檔案中心 (alipay.com)

相關文章