CDN API签名机制

授权头计算方法

Azure CDN restful API的每个请求都要进行身份验证,需要在请求中包含身份验证信息,协议为HTTPS。

身份验证信息以Authorization请求头发送,格式为"AzureCDN {Key ID}:{HMAC-SHA256 signature using Key Value}"。

Key ID和Key Value可以到Azure CDN新版管理门户的密钥管理处申请和管理。签名参数部分包括:请求绝对路径,用 ", "连接起来的按字母序从小到大排序的查询参数对,请求的UTC时间(与请求头中的x-azurecdn-request-date一样),大写的请求方法。以上几部分用回车换行连接后用Key Value进行HMAC-SHA256签名。

Authorization请求头生成示例

Python

def calculate_authorization_header(request_url, request_time, key_id, key_value, http_method):
    """ Calculate the authorization header.
    @request_url: Complete request URL with scheme, host, path and queries
    @request_time: UTC request time with format yyyy-MM-dd hh:mm:ss
    @key_id: API key ID
    @key_value: API key value
    @http_method: Http method in upper case

    """
    urlparts = urllib.parse.urlparse(request_url)
    queries = urllib.parse.parse_qs(urlparts.query)
    ordered_queries = OrderedDict(sorted(queries.items()))
    message = "%s\r\n%s\r\n%s\r\n%s" % (urlparts.path, ", ".join(['%s:%s' % (key, value[0]) for (key, value) in ordered_queries.items()]), request_time, http_method)
    digest = hmac.new(bytearray(key_value, "utf-8"), bytearray(message, "utf-8"), hashlib.sha256).hexdigest().upper()
    return "AzureCDN %s:%s" % (key_id, digest)

Java

/**
 * Calculate the authorization header
 *
 * @param  requestURL Complete request URL with scheme, host, path and queries
 * @param  requestTime UTC request time with format "yyyy-MM-dd hh:mm:ss"
 * @param  keyID API key ID
 * @param  keyValue API key value
 * @param  httpMethod HTTP method in upper case 
 * @return Calculated authorization header
 */
public static String calculateAuthorizationHeader(String requestURL, String requestTime, String keyID, String keyValue, String httpMethod) throws Exception {
              URL url = new URL(requestURL);
              String path = url.getPath();

              // Get query parameters
              String query = url.getQuery();      
              String[] params = query.split("&");
              Map<String, String> paramMap = new TreeMap<String, String>();
              for(String param: params) {
                    String[] paramterParts = param.split("=");
                    if(paramterParts.length != 2) {
                          continue;
                    }

                    paramMap.put(paramterParts[0], paramterParts[1]);
              }

              String orderedQueries = paramMap.entrySet()
                                              .stream()
                                              .map(entry -> entry.getKey() + ":" + entry.getValue())
                                              .collect(Collectors.joining(", "));

              String content = String.format("%s\r\n%s\r\n%s\r\n%s", path, orderedQueries, requestTime, httpMethod);        
              Mac sha256HMAC = Mac.getInstance("HmacSHA256");
              SecretKeySpec secret_key = new SecretKeySpec(keyValue.getBytes(), "HmacSHA256");
              sha256HMAC.init(secret_key);
              byte[] bytes = sha256HMAC.doFinal(content.getBytes());

              StringBuffer hash = new StringBuffer();
              for (int i = 0; i < bytes.length; i++) {
                  String hex = Integer.toHexString(0xFF & bytes[i]);
                  if (hex.length() == 1) {
                    hash.append('0');
                  }
                  hash.append(hex);
              }

              return String.format("AzureCDN %s:%s", keyID, hash.toString().toUpperCase());
}

Go

func calculateAuthorizationHeader(requestURL, requestTime, keyID, keyValue, httpMethod string) string {
            u, err := url.Parse(requestURL)
            if err != nil {
                panic(err)
            }

            var path = u.Path
            m, _ := url.ParseQuery(u.RawQuery)

            var keys []string
            for k := range m {
                            keys = append(keys, k)
            }
            sort.Strings(keys)
            var orderedQueries []string
            for _, k := range keys {
                            orderedQueries = append(orderedQueries, fmt.Sprintf("%s:%s", k, m[k][0]))
            }

            var queries = strings.Join(orderedQueries, ", ")
            content := fmt.Sprintf("%s\r\n%s\r\n%s\r\n%s", path, queries, requestTime, httpMethod)
            hash := hmac.New(sha256.New, []byte(keyValue))
            hash.Write([]byte(content))
            digest := strings.ToUpper(hex.EncodeToString(hash.Sum(nil)))
            return fmt.Sprintf("AzureCDN %s:%s", keyID, digest)
 }

C Sharp

/// <summary>
/// Calculate the authorization header.
/// </summary>
/// <param name="requestUrl">Complete request URL with scheme, host, path and queries</param>
/// <param name="requestTime">UTC request time with format yyyy-MM-dd hh:mm:ss.</param>
/// <param name="keyID">The API key ID.</param>
/// <param name="keyValue">The API key value.</param>
/// <param name="httpMethod">Http method in upper case</param>
/// <returns>Calculated authorization header</returns>
public static string CalculateAuthorizationHeader(string requestUrl, string requestTime, string keyID, string keyValue, string httpMethod)
{
          Uri requestUri = new Uri(requestUrl);

          StringBuilder hashContentBuilder = new StringBuilder();
          hashContentBuilder.Append(requestUri.AbsolutePath.ToLowerInvariant());
          hashContentBuilder.Append("\r\n");

          var queryStrings = HttpUtility.ParseQueryString(requestUri.Query);
          var sortedParameterNames = queryStrings.AllKeys.ToList();
          sortedParameterNames.Sort((q1, q2) => string.Compare(q1, q2));
          var result = string.Join(", ", sortedParameterNames.Select(p => string.Format("{0}:{1}", p, queryStrings[p])).ToArray());
          if (!string.IsNullOrEmpty(result))
          {
              hashContentBuilder.Append(result);
              hashContentBuilder.Append("\r\n");
          }

          hashContentBuilder.Append(requestTime);
          hashContentBuilder.Append("\r\n");
          hashContentBuilder.Append(httpMethod.ToUpper());
          string hashContent = hashContentBuilder.ToString();

          using (HMACSHA256 myhmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(keyValue)))
          {
              byte[] byteArray = Encoding.UTF8.GetBytes(hashContent);
              byte[] hashedValue = myhmacsha256.ComputeHash(byteArray);

              string sbinary = string.Empty;
              for (int i = 0; i < hashedValue.Length; i++)
              {
                  sbinary += hashedValue[i].ToString("X2");
              }

              return string.Format("AzureCDN {0}:{1}", keyID, sbinary);
          }
}