全部产品
云市场

签名机制

更新时间:2019-04-19 10:42:38

概述

对于每一次 HTTP 或者 HTTPS 协议请求,我们会根据访问中的签名信息验证访问请求者身份。具体由使用 AccessKeyID 和 AccessKeySecret 对称加密验证实现。

AccessKeyID 和 AccessKeySecret 可在控制台 AccessKey 管理 页面获得。其中 AccessKeyID 是访问者身份,AccessKeySecret 是加密签名字符串和服务器端验证签名字符串的密钥,必须严格保密谨防泄露。

说明:我们提供了多种编程语言的 SDK 及第三方 SDK,可以免去您签名的烦恼。更多详情,请下载 SDK

步骤 1. 构造规范化请求字符串

  1. 排序参数。排序规则以首字母顺序排序,排序参数包括 公共请求参数 和接口自定义参数,不包括公共请求参数中的 Signature 参数。

    说明:当使用 GET 方法提交请求时,这些参数就是请求 URL 中的参数部分。即 URL 中 ? 之后由 & 连接的部分。

  2. 编码参数。使用 UTF-8 字符集按照 RFC3986 规则编码请求参数和参数取值,编码规则如下:

    • 字符 A~Z、a~z、0~9 以及字符 -_.~ 不编码。

    • 其它字符编码成 %XY 的格式,其中 XY 是字符对应 ASCII 码的 16 进制。示例:半角双引号(")对应 %22

    • 扩展的 UTF-8 字符,编码成 %XY%ZA… 的格式。

    • 空格( )编码成 %20,而不是加号(+)。

      该编码方式与 application/x-www-form-urlencoded MIME 格式编码算法相似,但又有所不同。

      如果您使用的是 Java 标准库中的 java.net.URLEncoder,可以先用标准库中 percentEncode 编码,随后将编码后的字符中加号(+)替换为 %20、星号(*)替换为 %2A%7E 替换为波浪号(~),即可得到上述规则描述的编码字符串。

  3. 使用等号(=)连接编码后的请求参数和参数取值。

  4. 使用与号(&)连接编码后的请求参数,注意参数排序与 步骤 1 一致。

步骤 2. 构造签名字符串

  1. 构造待签名字符串 StringToSign。

    您可以同样使用 percentEncode 处理上一步构造的规范化请求字符串,规则如下:

    1. StringToSign=
    2. HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。
    3. percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。
    4. percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。
  2. 计算 HMAC 值。

    按照 RFC2104 的定义,使用上述步骤得到的字符串计算签名 HMAC 值。

    注意:计算签名时使用的 Key 就是您持有的 AccessKeySecret 并加上一个 & 字符(ASCII:38),使用的哈希算法是 SHA1。

  3. 计算签名值。

    按照 Base64 编码规则 把上一步骤中的 HMAC 值编码成字符串,即得到签名值(Signature)。

  4. 添加签名。

    将得到的签名值作为 Signature 参数添加到请求参数中,即完成请求签名过程。

    注意:得到的签名值在作为最后的请求参数值提交给服务器时,也要按照 RFC3986 规则进行 URL 编码。

请求示例

以 GetVideoPlayAuth 为例,假设使用的 AccessKeyIdtestAccessKeyIdAccessKeySecrettestAccessKeySecret。 那么签名前的请求 URL 为:

  1. http://vod.cn-shanghai.aliyuncs.com/?TimeStamp=2017-10-10T12:02:54Z&Format=JSON&AccessKeyId=testAccessKeyId&Action=GetVideoPlayAuth&SignatureMethod=HMAC-SHA1&SignatureNonce=8f8a035d-6496-4268-afd4-67c22837e38d&Version=2017-03-21&SignatureVersion=1.0&VideoId=5aed81b74ba84920be578cdfe004af4b

计算得到的待签名字符串 StringToSign 为:

  1. GET&%2F&AccessKeyId%3DtestAccessKeyId&Action%3DGetVideoPlayAuth&Format%3DJSON&SignatureMethod%3DHMAC-SHA1&SignatureNonce%3D8f8a035d-6496-4268-afd4-67c22837e38d&SignatureVersion%3D1.0&Timestamp%3D2017-10-10T12%253A02%253A54Z&Version%3D2017-03-21&VideoId%3D5aed81b74ba84920be578cdfe004af4b

因为 AccessKeySecrettestAccessKeySecret,所以用于计算 HMAC 的 Key 为 testAccessKeySecret&,计算得到的签名值为:

  1. Ibgh7y8Vp47LBuAsf5Xhi1SvDss%3D

将签名作为 Signature 参数加入到 URL 请求中,最后得到的 URL 为:

  1. http://vod.cn-shanghai.aliyuncs.com?AccessKeyId=testAccessKeyId&Action=GetVideoPlayAuth&Format=JSON&SignatureMethod=HMAC-SHA1&SignatureNonce=8f8a035d-6496-4268-afd4-67c22837e38d&SignatureVersion=1.0&Timestamp=2017-10-10T12%3A02%3A54Z&Version=2017-03-21&VideoId=5aed81b74ba84920be578cdfe004af4b&Signature=Ibgh7y8Vp47LBuAsf5Xhi1SvDss%3D

Java 示例代码

以下将为您介绍OpenAPI公共参数中需要您通过代码生成的参数,其他参数请根据文档填写具体值(本示例不需要依赖第三方的库包,可以直接使用)。

1. 生成签名串Signature

构造签名字符串分为以下几个步骤进行:

1.1. 构造规范化的请求字符串

参数排序及URL编码。其中,参数排序是指将参数名称以字母序升序排列。

  1. /*对所有参数名称和参数值做URL编码*/
  2. public static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
  3. List<String> encodeParams = new ArrayList<String>();
  4. if (publicParams != null) {
  5. for (String key : publicParams.keySet()) {
  6. String value = publicParams.get(key);
  7. //将参数和值都urlEncode一下。
  8. String encodeKey = percentEncode(key);
  9. String encodeVal = percentEncode(value);
  10. encodeParams.add(encodeKey + "=" + encodeVal);
  11. }
  12. }
  13. if (privateParams != null) {
  14. for (String key : privateParams.keySet()) {
  15. String value = privateParams.get(key);
  16. //将参数和值都urlEncode一下。
  17. String encodeKey = percentEncode(key);
  18. String encodeVal = percentEncode(value);
  19. encodeParams.add(encodeKey + "=" + encodeVal);
  20. }
  21. }
  22. return encodeParams;
  23. }
  24. /*获取 CanonicalizedQueryString*/
  25. public static String getCQS(List<String> allParams) {
  26. ParamsComparator paramsComparator = new ParamsComparator();
  27. Collections.sort(allParams, paramsComparator);
  28. String cqString = "";
  29. for (int i = 0; i < allParams.size(); i++) {
  30. cqString += allParams.get(i);
  31. if (i != allParams.size() - 1) {
  32. cqString += "&";
  33. }
  34. }
  35. return cqString;
  36. }
  37. /*字符串参数比较器,按字母序升序*/
  38. public static class ParamsComparator implements Comparator<String> {
  39. @Override
  40. public int compare(String lhs, String rhs) {
  41. return lhs.compareTo(rhs);
  42. }
  43. }

1.2. 构造待签名的字符串

  1. /*构造待签名的字符串*/
  2. String StringToSign = httpMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString);
  3. /*特殊字符替换为转义字符*/
  4. public static String percentEncode(String value) {
  5. try {
  6. String urlEncodeOrignStr = URLEncoder.encode(value, "UTF-8");
  7. String plusReplaced = urlEncodeOrignStr.replace("+", "%20");
  8. String starReplaced = plusReplaced.replace("*", "%2A");
  9. String waveReplaced = starReplaced.replace("%7E", "~");
  10. return waveReplaced;
  11. } catch (UnsupportedEncodingException e) {
  12. e.printStackTrace();
  13. }
  14. return value;
  15. }

1.3. 计算待签名字符串的HMAC值

  1. public static byte[] hmacSHA1Signature(String accessKeySecret, String stringToSign) {
  2. try {
  3. String key = accessKeySecret + "&";
  4. try {
  5. SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
  6. Mac mac = Mac.getInstance("HmacSHA1");
  7. mac.init(signKey);
  8. return mac.doFinal(stringToSign.getBytes());
  9. } catch (Exception e) {
  10. throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
  11. }
  12. } catch (SignatureException e) {
  13. e.printStackTrace();
  14. }
  15. return null;
  16. }

1.4. 编码得到最终签名值

按照 Base64 编码规则将1.3中计算得到的HMAC值编码成字符串,得到最终签名值(Signature)。

  1. public static String newStringByBase64(byte[] bytes)
  2. throws UnsupportedEncodingException {
  3. if (bytes == null || bytes.length == 0) {
  4. return null;
  5. }
  6. return new String(new BASE64Encoder().encode(bytes));
  7. }

2. 生成时间戳TimeStamp

生成TimeStamp,为UTC时间戳,如:2017-10-10T12:02:54Z

  1. /*生成当前UTC时间戳Time*/
  2. public static String generateTimestamp() {
  3. Date date = new Date(System.currentTimeMillis());
  4. SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
  5. df.setTimeZone(new SimpleTimeZone(0, "GMT"));
  6. return df.format(date);
  7. }

3. 生成随机数SignatureNonce

  1. public static String generateRandom() {
  2. String signatureNonce = UUID.randomUUID().toString();
  3. return signatureNonce;
  4. }

至此,已经为您重点介绍了如何生成OpenAPI核心参数的代码示例,为了方便您快速使用签名机制,您可以在 OpenAPI调用示例 下载完整的OpenAPI调用示例Java代码。

更多参考

服务端SDK

可以直接使用服务端接口SDK,来避免自行实现签名机制,SDK支持以下语言版本:

服务端接口SDK封装了 点播服务API 的使用,也提供了各接口的代码示例。

其它签名示例

如确实想自行计算签名,可参考阿里云SDK的签名实现代码: