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值为全小写):
POSTZDgxNjY5ZjFlMDQ5MGM0YWMwMWE5ODlmZDVlYmQxYjI=text/xml;charset=utf-8Wed, 25 May 2016 10:46:14 GMTx-mns-request-id:57458276F0E3D56D7C00054Bx-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 {//获取证书的URLif (!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-阿里云