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 地址,再從中提取出簽名的公鑰。
第二步:計算待簽名字符串
VERB + "n"
+ CONTENT-MD5 + "n"
+ CONTENT-TYPE + "n"
+ DATE + "n"
+ CanonicalizedMNSHeaders
+ CanonicalizedResource
VERB
表示HTTP的MethodCONTENT-MD5
表示請求內容數據的MD5值CONTENT-TYPE
表示請求內容的類型,對應的值為全小寫DATE
表示此次操作的時間,不能為空,目前隻支持GMT格式CanonicalizedMNSHeaders
表示 http 請求 Header 中的x-mns-
開始的字段組合(見下文注意事項)CanonicalizedResource
表示 http 請求的相對地址,不能為空
待簽名字符串示例(注:不應該出現空的行全小寫,Content-Type值為全小寫):
POST
ZDgxNjY5ZjFlMDQ5MGM0YWMwMWE5ODlmZDVlYmQxYjI=
text/xml;charset=utf-8
Wed, 25 May 2016 10:46:14 GMT
x-mns-request-id:57458276F0E3D56D7C00054B
x-mns-signing-cert-url:aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5wZW0=
x-mns-version:2015-06-06
/notifications
第三步:Authorization 解密
對Authorization 簽名字段進行 Base64 Decode 解碼後,使用從第一步從證書中提取的公鑰對其進行解密;
第四步:認證
比較第二步生成的待簽名字符串與第三步解密的結果是否一致。如果一致,則表明請求來自MNS,訪問合法。
注意事項
CanonicalizedMNSHeaders(即
x-mns-
開頭的head)在簽名驗證前需要符合以下規範:
- head 的名字需要變成小寫
- head 自小到大排序
- 分割 head name 和 value 的冒號前後不能有空格
- 每個Head之後都有一個
n
,如果沒有以x-mns-
開頭的head,則在簽名時CanonicalizedMNSHeaders就設置為空
其他
- 用來簽名的字符串為 UTF-8 格式;
- 簽名的方法用 RFC 3447 (https://tools.ietf.org/html/rfc3447)中定義的 sha1WithRSAEncryption 方法;
- Base64 是指使用 base64 算法轉碼文本。
Java示例代碼
public class SignDemo {
private Boolean authenticate(String method, String uri, Map<String, String> headers) {
try {
//獲取證書的URL
if (!headers.containsKey("x-mns-signing-cert-url")) {
System.out.println("x-mns-signing-cert-url Header not found");
return false;
}
String cert = headers.get("x-mns-signing-cert-url");
if (cert.isEmpty()) {
System.out.println("x-mns-signing-cert-url empty");
return false;
}
cert = new String(Base64.decodeBase64(cert));
System.out.println("x-mns-signing-cert-url:t" + cert);
//根據URL獲取證書,並從證書中獲取公鑰
URL url = new URL(cert);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
DataInputStream in = new DataInputStream(conn.getInputStream());
CertificateFactory cf = CertificateFactory.getInstance("X.509");
Certificate c = cf.generateCertificate(in);
PublicKey pk = c.getPublicKey();
//獲取待簽名字符串
String str2sign = getSignStr(method, uri, headers);
System.out.println("String2Sign:t" + str2sign);
//對Authorization字段做Base64解碼
String signature = headers.get("Authorization");
byte[] decodedSign = Base64.decodeBase64(signature);
//認證
java.security.Signature signetcheck = java.security.Signature.getInstance("SHA1withRSA");
signetcheck.initVerify(pk);
signetcheck.update(str2sign.getBytes());
Boolean res = signetcheck.verify(decodedSign);
return res;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
private String getSignStr(String method, String uri, Map<String, String> headers) {
StringBuilder sb = new StringBuilder();
sb.append(method);
sb.append("n");
sb.append(safeGetHeader(headers, "Content-md5"));
sb.append("n");
sb.append(safeGetHeader(headers, "Content-Type"));
sb.append("n");
sb.append(safeGetHeader(headers, "Date"));
sb.append("n");
List<String> tmp = new ArrayList<String>();
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().startsWith("x-mns-")) {
tmp.add(entry.getKey() + ":" + entry.getValue());
}
}
Collections.sort(tmp);
for (String kv : tmp) {
sb.append(kv);
sb.append("n");
}
sb.append(uri);
return sb.toString();
}
private String safeGetHeader(Map<String, String> headers, String name) {
if (headers.containsKey(name)) {
return headers.get(name);
} else {
return "";
}
}
public static void main(String[] args) {
SignDemo sd = new SignDemo();
Map<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", "Mko2Azg9fhCw8qR6G7AeAFMyzjO9qn7LDA5/t9E+6X5XURXTqBUuhpK+K55UNhrnlE2UdDkRrwDxsaDP5ajQdg==");
headers.put("Content-md5", "M2ViOTE2ZDEyOTlkODBjMjVkNzM4YjNhNWI3ZWQ1M2E=");
headers.put("Content-Type", "text/xml;charset=utf-8");
headers.put("Date", "Tue, 23 Feb 2016 09:41:06 GMT");
headers.put("x-mns-request-id", "56CC2932F0E3D5BD530685CB");
headers.put("x-mns-signing-cert-url", "aHR0cDovL21uc3Rlc3Qub3NzLWNuLWhhbmd6aG91LmFsaXl1bmNzLmNvbS94NTA5X3B1YmxpY19jZXJ0aWZpY2F0ZS5wZW0=");
headers.put("x-mns-version", "2015-06-06");
Boolean res = sd.authenticate("POST", "/notifications", headers);
System.out.println("Authenticate result:" + res);
}
}
最後更新:2016-11-23 17:16:04
上一篇:
請求簽名機製__調用方式_API使用手冊_消息服務-阿裏雲
下一篇:
RESTfulAPI概述__隊列接口規範_API使用手冊_消息服務-阿裏雲
CDN監控__雲服務監控_用戶指南_雲監控-阿裏雲
OSS插件最佳功能組合__最佳實踐_Eclipse 插件-阿裏雲
VpcAttributesType__數據類型_API 參考_雲服務器 ECS-阿裏雲
修改RDS實例數據複製和高可用策略__實例管理_API 參考_雲數據庫 RDS 版-阿裏雲
ECS雲服務器帶寬FAQ
圖文識別同步調用接口__OCR圖文識別API_內容檢測API文檔_阿裏綠網-阿裏雲
AnalyticDB數據源配置__數據源配置_數據同步手冊_用戶操作指南_大數據開發套件-阿裏雲
優化建議__最佳實踐_雲數據庫 PetaData-阿裏雲
Topic操作__主題接口規範_API使用手冊_消息服務-阿裏雲
修改、查詢、刪除定時任務__定時任務_用戶指南_彈性伸縮-阿裏雲
相關內容
常見錯誤說明__附錄_大數據計算服務-阿裏雲
發送短信接口__API使用手冊_短信服務-阿裏雲
接口文檔__Android_安全組件教程_移動安全-阿裏雲
運營商錯誤碼(聯通)__常見問題_短信服務-阿裏雲
設置短信模板__使用手冊_短信服務-阿裏雲
OSS 權限問題及排查__常見錯誤及排除_最佳實踐_對象存儲 OSS-阿裏雲
消息通知__操作指南_批量計算-阿裏雲
設備端快速接入(MQTT)__快速開始_阿裏雲物聯網套件-阿裏雲
查詢API調用流量數據__API管理相關接口_API_API 網關-阿裏雲
使用STS訪問__JavaScript-SDK_SDK 參考_對象存儲 OSS-阿裏雲