對於每一次HTTP或者HTTPS協議請求,我們會根據訪問中的簽名資訊驗證訪問要求者身份。具體由使用AccessKeyID和AccessKeySecret對稱式加密驗證實現。

AccessKey相當於使用者密碼,AccessKey用於調用API,而使用者密碼用於登入 ECS控制台 。其中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替換為波浪號(~),即可得到上述規則描述的編碼字元串。

      private static final String ENCODING = "UTF-8";
      private static String percentEncode(String value) throws UnsupportedEncodingException {
      return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
      }
  3. 使用等號(=)串連編碼後的請求參數和參數取值。
  4. 使用與號(&)串連編碼後的請求參數,注意參數排序與 步驟1 一致。

現在,您得到了正常化請求字元串(CanonicalizedQueryString),其結構遵循 請求結構

步驟 2. 建構簽章字元串

  1. 構造待簽名字元串StringToSign。您可以同樣使用 percentEncode 處理上一步構造的正常化請求字元串,規則如下:
    StringToSign=
      HTTPMethod + "&" + //HTTPMethod:發送請求的 HTTP 方法,例如 GET。
      percentEncode("/") + "&" + //percentEncode("/"):字元(/)UTF-8 編碼得到的值,即 %2F。
      percentEncode(CanonicalizedQueryString) //您的正常化請求字元串。
  2. 按照 RFC2104 的定義,計算待簽名字元串StringToSign的HMAC-SHA1值。樣本使用的是Java Base64編碼方法。
    Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )
    说明
    計算簽名時,RFC2104規定的Key值是您的 AccessKeySecret 並加上與號(&),其ASCII值為38。
  3. 添加根據 RFC3986 規則編碼後的參數 Signature 到正常化請求字元串URL中。

樣本 1. 參數拼接法

以調用 DescribeRegions 查詢地域為例。假設您獲得了AccessKeyID=testid以及AccessKeySecret=testsecret,簽名流程如下所示:

  1. 構造正常化請求字元串。
    http://ecs.aliyuncs.com/?Timestamp=2016-02-23T12:46:24Z&Format=XML&AccessKeyId=testid&Action=DescribeRegions&SignatureMethod=HMAC-SHA1&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2014-05-26&SignatureVersion=1.0
  2. 構造待簽名字元串 StringToSign
    GET&%2F&AccessKeyId%3Dtestid%26Action%3DDescribeRegions%26Format%3DXML%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf%26SignatureVersion%3D1.0%26Timestamp%3D2016-02-23T12%253A46%253A24Z%26Version%3D2014-05-26
  3. 計算簽名值。因為AccessKeySecret=testsecret,用於計算的Key為testsecret&,計算得到的簽名值為 OLeaidS1JvxuMvnyHOwuJ+uX5qY=。樣本使用的是Java Base64編碼方法。
    Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )
  4. 添加 RFC3986 規則編碼後的Signature=OLeaidS1JvxuMvnyHOwuJ%2BuX5qY%3D步驟1 的URL中。
    http://ecs.aliyuncs.com/?SignatureVersion=1.0&Action=DescribeRegions&Format=XML&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2014-05-26&AccessKeyId=testid&Signature=OLeaidS1JvxuMvnyHOwuJ%2BuX5qY%3D&SignatureMethod=HMAC-SHA1&Timestamp=2016-02-23T12%253A46%253A24Z

通過以上URL,您可以使用瀏覽器、curl或者wget等工具發起HTTP請求調用DescribeRegions,查看阿里雲的地域列表。

樣本 2. 程式設計語言法

依然以調用 DescribeRegions 查詢地域為例。假設您獲得了AccessKeyID=testid以及AccessKeySecret=testsecret,並且假定所有請求參數放在一個Java Map<String, String>對象裡。

  1. 預定義編碼方法。
    private static final String ENCODING = "UTF-8";
    private static String percentEncode(String value) throws UnsupportedEncodingException {
      return value != null ? URLEncoder.encode(value, ENCODING).replace("+", "%20").replace("*", "%2A").replace("%7E", "~") : null;
    }
  2. 預定義編碼時間格式Timestamp。參數Timestamp必須符合 ISO8601 規範,並需要使用UTC時間,時區為+0。
    private static final String ISO8601_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    private static String formatIso8601Date(Date date) {
      SimpleDateFormat df = new SimpleDateFormat(ISO8601_DATE_FORMAT);
      df.setTimeZone(new SimpleTimeZone(0, "GMT"));
      return df.format(date);
    }
  3. 構造請求字元串。
    final String HTTP_METHOD = "GET";
    Map parameters = new HashMap();
    // 輸入請求參數
    parameters.put("Action", "DescribeRegions");
    parameters.put("Version", "2014-05-26");
    parameters.put("AccessKeyId", "testid");
    parameters.put("Timestamp", formatIso8601Date(new Date()));
    parameters.put("SignatureMethod", "HMAC-SHA1");
    parameters.put("SignatureVersion", "1.0");
    parameters.put("SignatureNonce", UUID.randomUUID().toString());
    parameters.put("Format", "XML");
    // 排序請求參數
    String[] sortedKeys = parameters.keySet().toArray(new String[]{});
    Arrays.sort(sortedKeys);
    final String SEPARATOR = "&";
    // 構造 stringToSign 字元串
    StringBuilder stringToSign = new StringBuilder();
    stringToSign.append(HTTP_METHOD).append(SEPARATOR);
    stringToSign.append(percentEncode("/")).append(SEPARATOR);
    StringBuilder canonicalizedQueryString = new StringBuilder();
    for(String key : sortedKeys) {
    // 這裡注意編碼 key 和 value
      canonicalizedQueryString.append("&")
      .append(percentEncode(key)).append("=")
      .append(percentEncode(parameters.get(key)));
    }
    // 這裡注意編碼 canonicalizedQueryString
    stringToSign.append(percentEncode(
      canonicalizedQueryString.toString().substring(1)));
  4. 簽名。因為AccessKeySecret=testsecret,所以用於計算HMAC的Key為testsecret&,計算得到的簽名值為OLeaidS1JvxuMvnyHOwuJ%2BuX5qY%3D
    // 以下是一段計算簽名的範例程式碼
    final String ALGORITHM = "HmacSHA1";
    final String ENCODING = "UTF-8";
    key = "testsecret&";
    Mac mac = Mac.getInstance(ALGORITHM);
    mac.init(new SecretKeySpec(key.getBytes(ENCODING), ALGORITHM));
    byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING));
    String signature = new String(Base64.encodeBase64(signData));

    增加簽名參數後,按照 RFC3986 規則編碼後的URL如下所示:

    http://ecs.aliyuncs.com/?SignatureVersion=1.0&Action=DescribeRegions&Format=XML&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2014-05-26&AccessKeyId=testid&Signature=OLeaidS1JvxuMvnyHOwuJ%2BuX5qY%3D&SignatureMethod=HMAC-SHA1&Timestamp=2016-02-23T12%253A46%253A24Z
  5. 使用瀏覽器、curl或者wget等工具發送HTTP請求。
    <DescribeRegionsResponse>
     <Regions>
         <Region>
             <LocalName>青島節點</LocalName>
             <RegionId>cn-qingdao</RegionId>
         </Region>
         <Region>
             <LocalName>杭州節點</LocalName>
             <RegionId>cn-hangzhou</RegionId>
         </Region>
     </Regions>
     <RequestId>833C6B2C-E309-45D4-A5C3-03A7A7A48ACF</RequestId>
    </DescribeRegionsResponse>

返回結果列舉了地域和地域ID列表。如果您在提交請求時,指定參數Format=JSON,那麼返回結果的格式為JSON格式而不是XML格式。更多詳情,請參閱 返回結果