Content Delivery Network API signing mechanism

Authorization header calculation method

Every Azure Content Delivery Network RESTful API request needs authentication, so the request must include the authentication details and use the HTTPS protocol.

The authentication details are sent as an authorization request header in the format “AzureCDN {Key ID}:{HMAC-SHA256 signature using Key Value}”.

To apply for and manage the key ID and key value, you can use the key management section of the new version of the Azure Content Delivery Network Management Portal.The signature parameter section includes: the absolute path of the signature, the query parameter in ascending alphabetical order separated by commas (“,”), the UTC time of the request (the same as x-azurecdn-request-date in the request header), and the request method in capitals.The sections above are linked using carriage returns to change lines, and then signed with HMAC-SHA256 using the key value.

Authorization-request header generation example

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);
          }
}