簽名
某些情況下(例如使用者掃碼支付成功時),支付寶會給商戶系統傳送非同步通知。在傳送非同步通知時,支付寶會對通知引數進行簽名,並將 “簽名字串 sign” 作為通知引數傳送給商戶系統。支付寶簽名的步驟是:
- 拼接 “待簽名字串”;
- 呼叫簽名方法 sign();
- 拼接完整的請求 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¬ify_time=2016-07-19 14:10:49&subject=大樂透2.1&sign_type=RSA2&charset=utf-8¬ify_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¬ify_id=4a91b7a78a503640467525113fb7d8bg8e
1 、拼接 “待簽名字串”
支付寶對要傳送的通知引數進行簽名的第一步是:拼接 “待簽名字串”。
- 對需要簽名的引數進行字典排序:字典排序是按照引數的第一個字元的 ASCII 碼遞增排序(字母升序排序)。如果引數的第一個字元相同,則按照引數的第二個字元的 ASCII 碼遞增排序,以此類推。
- 對排序後的引數進行拼接,得到 “待簽名字串”:將排序後的引數與其對應值,組合成
引數=引數值
的格式,引數與引數之間用 & 字元連線起來,此時生成的字串為 “待簽名字串”。
2、呼叫簽名方法 sign()
支付寶對要傳送的通知引數進行簽名的第二步是:呼叫簽名演演算法對應的簽名方法 sign(),生成簽名字串 sign。
呼叫簽名演演算法對應的簽名方法時,需要提供以下幾個引數:待簽名字串 content、字元編碼方案 charset(UTF-8、GBK)和 簽名者的私鑰 privateKey。簽名方法 sign() 的處理邏輯如下:
- 對 “待簽名字串” 進行編碼,得到位元組陣列:使用指定的字元編碼方案,將 “待簽名字串” 編碼為位元組陣列(byte 型別的陣列)
- 進行簽名、Base64 編碼,得到簽名字串 sign:使用簽名者的私鑰 privateKey 對 “待簽名字串” 對應的位元組陣列進行簽名,並對簽名結果進行 Base64 編碼,以便在網路上傳輸。經過 Base64 編碼後的字串即為 “簽名字串 sign”。
String sign = new String(Base64.encodeBase64(signed));
3、拼接完整的請求 URL
支付寶對要傳送的通知引數進行簽名的第三步是:拼接完整的請求 URL。
- 拼接 sign 引數:將生成的簽名字串作為 sign 引數的 value 拼接到請求資料中。
- Encode 請求資料:對所有一級 key 的 value 值進行 UrlEncode 編碼。
- 拼接完整的請求 URL:將編碼後的請求資料傳送至商戶系統指定的 URL。
驗籤
驗籤的全稱是:驗證指定內容的簽名是否正確。
商戶系統收到支付寶傳送的非同步通知後,商戶系統需要對支付寶傳送的通知引數進行驗籤處理。商戶系統驗籤的步驟是:
- 拼接 “待驗簽字串”;
- 獲取簽名字串 sign;
- 呼叫驗籤方法 verify(),獲得驗簽結果。
1、拼接 “待驗簽字串”
對支付寶傳送的通知引數進行驗籤的第一步是:拼接 “待驗簽字串”。
- 獲取簽名的引數:在通知返回的引數列表中,除去 sign、sign_type 兩個引數外,凡是通知返回的引數都是簽名的引數。
- 對簽名的引數進行 UrlDecode 解碼
- 對簽名的引數進行字典排序:字典排序是按照引數的第一個字元的 ASCII 碼遞增排序(字母升序排序)。如果引數的第一個字元相同,則按照引數的第二個字元的 ASCII 碼遞增排序,以此類推。
- 對排序後的引數進行拼接,得到 “待驗簽字串”:將排序後的引數與其對應值,組合成
引數=引數值
的格式,引數與引數之間用 & 字元連線起來,此時生成的字串為 “待驗簽字串”。
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¬ify_time=2016-07-19 14:10:49¬ify_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() 的處理邏輯如下:
- 對 “待驗簽字串” 進行編碼,得到位元組陣列:使用指定的字元編碼方案,將 “待驗籤符串” 編碼為位元組陣列(byte 型別的陣列)
- 對 “簽名字串 sign” 進行 Base64 解碼,得到位元組陣列:得到的這個位元組陣列即為當時簽名之後的初始位元組陣列(密文)。
- 使用公鑰驗證簽名:
boolean varifyResult = boosignature.verify(Base64.decodeBase64(sign.getBytes()))
- 使用簽名者的公鑰 publicKey 對 密文 進行 解密,得到 “支付寶通知的摘要”;
- 對 “待驗籤符串” 對應的位元組陣列做摘要,得到摘要結果;
- 將 “商戶計算出的摘要結果” 和 “解密得到的摘要” 作比較:如果二者相同,則說明驗籤成功;否則說明驗籤失敗。
驗籤成功後,商戶系統根據通知引數進行業務處理。