全部产品

签名机制

您可以阅读本文,了解不使用SDK而直接使用HTTP/HTTPS方式调用API的签名机制。

概述

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

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

说明

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

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

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

说明

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

  1. 编码参数。使用 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 替换为波浪号(~),即可得到上述规则描述的编码字符串。

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

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

步骤 2. 构造签名字符串

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

StringToSign=
HTTPMethod + "&" + //HTTPMethod:发送请求的 HTTP 方法,例如 GET。
percentEncode("/") + "&" + //percentEncode("/"):字符(/)UTF-8 编码得到的值,即 %2F。
percentEncode(CanonicalizedQueryString) //您的规范化请求字符串。
  1. 计算 HMAC 值。按照 RFC2104 的定义,使用上述步骤得到的字符串计算签名 HMAC 值。

说明

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

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

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

说明

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

请求示例

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

http://ice.cn-shanghai.aliyuncs.com/?TimeStamp=2020-12-28T12:38:25Z&Format=JSON&AccessKeyId=testAccessKeyId&Action=GetEditingProject&SignatureMethod=HMAC-SHA1&SignatureNonce=526bd033-a426-492c-aafb-1404d8d07ace&Version=2020-11-09&SignatureVersion=1.0&ProjectId=64a64d349eac4489bf95d485a73b9cef

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

GET&%2F&AccessKeyId%3DtestAccessKeyId%26Action%3DGetEditingProject%26Format%3DJSON%26Project%3D64a64d349eac4489bf95d485a73b9cef%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D526bd033-a426-492c-aafb-1404d8d07ace%26SignatureVersion%3D1.0%26Timestamp%3D2020-12-28T12%253A38%253A25Z%26Version%3D2020-11-09

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

%2Fb%2FQblisruV3ZGUCG048LTsKHB8%3D

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

http://ice.cn-shanghai.aliyuncs.com?AccessKeyId=testAccessKeyId&Action=GetEditingProject&Format=JSON&Project=64a64d349eac4489bf95d485a73b9cef&SignatureMethod=HMAC-SHA1&SignatureNonce=526bd033-a426-492c-aafb-1404d8d07ace&SignatureVersion=1.0&Timestamp=2020-12-28T12%3A38%3A25Z&Version=2020-11-09&Signature=%2Fb%2FQblisruV3ZGUCG048LTsKHB8%3D

Java 示例代码

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

1. 生成签名串Signature

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

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

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

/*对所有参数名称和参数值做URL编码*/
public static List<String> getAllParams(Map<String, String> publicParams, Map<String, String> privateParams) {
    List<String> encodeParams = new ArrayList<String>();
    if (publicParams != null) {
        for (String key : publicParams.keySet()) {
            String value = publicParams.get(key);
            //将参数和值都urlEncode一下。
            String encodeKey = percentEncode(key);
            String encodeVal = percentEncode(value);
            encodeParams.add(encodeKey + "=" + encodeVal);
        }
    }
    if (privateParams != null) {
        for (String key : privateParams.keySet()) {
            String value = privateParams.get(key);
            //将参数和值都urlEncode一下。
            String encodeKey = percentEncode(key);
            String encodeVal = percentEncode(value);
            encodeParams.add(encodeKey + "=" + encodeVal);
        }
    }
    return encodeParams;
}
/*获取 CanonicalizedQueryString*/
public static String getCQS(List<String> allParams) {
    ParamsComparator paramsComparator = new ParamsComparator();
    Collections.sort(allParams, paramsComparator);
    String cqString = "";
    for (int i = 0; i < allParams.size(); i++) {
        cqString += allParams.get(i);
        if (i != allParams.size() - 1) {
            cqString += "&";
        }
    }
    return cqString;
}
/*字符串参数比较器,按字母序升序*/
public static class ParamsComparator implements Comparator<String> {
    @Override
    public int compare(String lhs, String rhs) {
        return lhs.compareTo(rhs);
    }
}

1.2. 构造待签名的字符串

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

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

public static byte[] hmacSHA1Signature(String accessKeySecret, String stringToSign) {
    try {
        String key = accessKeySecret + "&";
        try {
              SecretKeySpec signKey = new SecretKeySpec(key.getBytes(), "HmacSHA1");
              Mac mac = Mac.getInstance("HmacSHA1");
              mac.init(signKey);
              return mac.doFinal(stringToSign.getBytes());
        } catch (Exception e) {
              throw new SignatureException("Failed to generate HMAC : " + e.getMessage());
        }
    } catch (SignatureException e) {
        e.printStackTrace();
    }
    return null;
}

1.4. 编码得到最终签名值

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

public static String newStringByBase64(byte[] bytes)
         throws UnsupportedEncodingException {
    if (bytes == null || bytes.length == 0) {
        return null;
    }
    return new String(new BASE64Encoder().encode(bytes));
}

2. 生成时间戳TimeStamp

生成TimeStamp,为UTC时间戳,如:2020-12-28T12:38:25Z

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

3. 生成随机数SignatureNonce

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

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

更多参考

服务端SDK

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

服务端接口SDK封装了 智能媒体生产API 的使用,也提供了各接口的代码示例。

其它签名示例

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