閱讀294 返回首頁    go 小米 go MIUI米柚


Endpoint簽名認證__調用方式_API使用手冊_消息服務-阿裏雲

用戶可以通過推送請求Header中的 x-mns-signing-cert-url 獲取簽名證書,並根據相應的方法來驗證該請求是否由MNS係統發出,防止惡意請求對用戶造成負麵影響。

在 MNS 推送請求的 Header 中,Authorization 字段的值是MNS根據待簽名字符串,用 SHA1-RSA 簽名算法生成簽名。Endpoint 可以使用公鑰對簽名進行驗證,具體的驗證方法如下:

第一步:獲取X509證書

在 MNS 發送給 Endpoint 的 http 請求 Header 中,x-mns-signing-cert-url 指定了簽名證書的地址( Base64 編碼),用戶需要通過 Base64 解碼,獲取該簽名文件 URL 地址,再從中提取出簽名的公鑰。

第二步:計算待簽名字符串

  1. VERB + "n"
  2. + CONTENT-MD5 + "n"
  3. + CONTENT-TYPE + "n"
  4. + DATE + "n"
  5. + CanonicalizedMNSHeaders
  6. + CanonicalizedResource
  • VERB 表示HTTP的Method
  • CONTENT-MD5 表示請求內容數據的MD5值
  • CONTENT-TYPE 表示請求內容的類型,對應的值為全小寫
  • DATE 表示此次操作的時間,不能為空,目前隻支持GMT格式
  • CanonicalizedMNSHeaders 表示 http 請求 Header 中的x-mns-開始的字段組合(見下文注意事項)
  • CanonicalizedResource表示 http 請求的相對地址,不能為空

待簽名字符串示例(注:不應該出現空的行全小寫,Content-Type值為全小寫):

  1. POST
  2. ZDgxNjY5ZjFlMDQ5MGM0YWMwMWE5ODlmZDVlYmQxYjI=
  3. text/xml;charset=utf-8
  4. Wed, 25 May 2016 10:46:14 GMT
  5. x-mns-request-id:57458276F0E3D56D7C00054B
  6. x-mns-signing-cert-url:aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5wZW0=
  7. x-mns-version:2015-06-06
  8. /notifications

第三步:Authorization 解密

對Authorization 簽名字段進行 Base64 Decode 解碼後,使用從第一步從證書中提取的公鑰對其進行解密;

第四步:認證

比較第二步生成的待簽名字符串與第三步解密的結果是否一致。如果一致,則表明請求來自MNS,訪問合法。

注意事項

CanonicalizedMNSHeaders(即x-mns-開頭的head)在簽名驗證前需要符合以下規範:

  • head 的名字需要變成小寫
  • head 自小到大排序
  • 分割 head name 和 value 的冒號前後不能有空格
  • 每個Head之後都有一個n,如果沒有以x-mns-開頭的head,則在簽名時CanonicalizedMNSHeaders就設置為空

其他

  1. 用來簽名的字符串為 UTF-8 格式;
  2. 簽名的方法用 RFC 3447 (https://tools.ietf.org/html/rfc3447)中定義的 sha1WithRSAEncryption 方法;
  3. Base64 是指使用 base64 算法轉碼文本。

Java示例代碼

  1. public class SignDemo {
  2. private Boolean authenticate(String method, String uri, Map<String, String> headers) {
  3. try {
  4. //獲取證書的URL
  5. if (!headers.containsKey("x-mns-signing-cert-url")) {
  6. System.out.println("x-mns-signing-cert-url Header not found");
  7. return false;
  8. }
  9. String cert = headers.get("x-mns-signing-cert-url");
  10. if (cert.isEmpty()) {
  11. System.out.println("x-mns-signing-cert-url empty");
  12. return false;
  13. }
  14. cert = new String(Base64.decodeBase64(cert));
  15. System.out.println("x-mns-signing-cert-url:t" + cert);
  16. //根據URL獲取證書,並從證書中獲取公鑰
  17. URL url = new URL(cert);
  18. HttpURLConnection conn = (HttpURLConnection) url.openConnection();
  19. DataInputStream in = new DataInputStream(conn.getInputStream());
  20. CertificateFactory cf = CertificateFactory.getInstance("X.509");
  21. Certificate c = cf.generateCertificate(in);
  22. PublicKey pk = c.getPublicKey();
  23. //獲取待簽名字符串
  24. String str2sign = getSignStr(method, uri, headers);
  25. System.out.println("String2Sign:t" + str2sign);
  26. //對Authorization字段做Base64解碼
  27. String signature = headers.get("Authorization");
  28. byte[] decodedSign = Base64.decodeBase64(signature);
  29. //認證
  30. java.security.Signature signetcheck = java.security.Signature.getInstance("SHA1withRSA");
  31. signetcheck.initVerify(pk);
  32. signetcheck.update(str2sign.getBytes());
  33. Boolean res = signetcheck.verify(decodedSign);
  34. return res;
  35. } catch (Exception e) {
  36. e.printStackTrace();
  37. return false;
  38. }
  39. }
  40. private String getSignStr(String method, String uri, Map<String, String> headers) {
  41. StringBuilder sb = new StringBuilder();
  42. sb.append(method);
  43. sb.append("n");
  44. sb.append(safeGetHeader(headers, "Content-md5"));
  45. sb.append("n");
  46. sb.append(safeGetHeader(headers, "Content-Type"));
  47. sb.append("n");
  48. sb.append(safeGetHeader(headers, "Date"));
  49. sb.append("n");
  50. List<String> tmp = new ArrayList<String>();
  51. for (Map.Entry<String, String> entry : headers.entrySet()) {
  52. if (entry.getKey().startsWith("x-mns-")) {
  53. tmp.add(entry.getKey() + ":" + entry.getValue());
  54. }
  55. }
  56. Collections.sort(tmp);
  57. for (String kv : tmp) {
  58. sb.append(kv);
  59. sb.append("n");
  60. }
  61. sb.append(uri);
  62. return sb.toString();
  63. }
  64. private String safeGetHeader(Map<String, String> headers, String name) {
  65. if (headers.containsKey(name)) {
  66. return headers.get(name);
  67. } else {
  68. return "";
  69. }
  70. }
  71. public static void main(String[] args) {
  72. SignDemo sd = new SignDemo();
  73. Map<String, String> headers = new HashMap<String, String>();
  74. headers.put("Authorization", "Mko2Azg9fhCw8qR6G7AeAFMyzjO9qn7LDA5/t9E+6X5XURXTqBUuhpK+K55UNhrnlE2UdDkRrwDxsaDP5ajQdg==");
  75. headers.put("Content-md5", "M2ViOTE2ZDEyOTlkODBjMjVkNzM4YjNhNWI3ZWQ1M2E=");
  76. headers.put("Content-Type", "text/xml;charset=utf-8");
  77. headers.put("Date", "Tue, 23 Feb 2016 09:41:06 GMT");
  78. headers.put("x-mns-request-id", "56CC2932F0E3D5BD530685CB");
  79. headers.put("x-mns-signing-cert-url", "aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5wZW0=");
  80. headers.put("x-mns-version", "2015-06-06");
  81. Boolean res = sd.authenticate("POST", "/notifications", headers);
  82. System.out.println("Authenticate result:" + res);
  83. }
  84. }

最後更新:2016-11-23 17:16:04

  上一篇:go 請求簽名機製__調用方式_API使用手冊_消息服務-阿裏雲
  下一篇:go RESTfulAPI概述__隊列接口規範_API使用手冊_消息服務-阿裏雲