Signature mechanism

Updated at:

To ensure the security of API calls, Alibaba Cloud authenticates each API request with a signature. All requests submitted over HTTP or HTTPS must include signature information.

Signature method

To sign a request, you must use your AccessKey ID and AccessKey secret to perform symmetric encryption. You can obtain your AccessKey ID and AccessKey secret from the AccessKey Management page in the Alibaba Cloud console. The AccessKey ID identifies the user. The AccessKey secret is the key used to encrypt the signature string and allow the server to verify the signature string. You must keep your AccessKey secret confidential.

Follow these steps to sign the request:

  1. Construct a canonicalized query string.

    1. Sorting parameter.

      Sort the request parameters in alphabetical order by parameter name. The parameters include common request parameters (excluding the Signature parameter) and API-specific custom parameters.

      Note

      For GET requests, these parameters are the part of the request URL that follows the question mark (?) and are connected by ampersands (&).

    2. URL-encode the parameter names and values.

      Use the UTF-8 character set to encode the request parameter names and values according to RFC3986. The encoding rules are as follows:

      • Uppercase letters A to Z, lowercase letters a to z, digits 0 to 9, and the characters -, _, ., and ~ are not encoded.

      • Other characters are encoded into the %XY format. XY represents the hexadecimal value of the character's ASCII code. For example, the double quotation mark (") is encoded as %22.

      • Extended UTF-8 characters are encoded into the %XY%ZA… format.

      • Spaces are encoded as %20 instead of plus signs (+).

      This encoding method is similar to, but different from, the application/x-www-form-urlencoded Multipurpose Internet Mail Extensions (MIME) format encoding algorithm.

      If you use java.net.URLEncoder from the Java standard library, you can first encode the string using the percentEncode method. Then, in the encoded string, replace plus signs (+) with %20, asterisks (*) with %2A, and %7E with tildes (~). This produces an encoded string that follows the rules.

      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. Use an equal sign (=) to connect the encoded parameter name and value.

    4. Use an ampersand (&) to connect the encoded request parameters. The parameter order must be the same as the alphabetical order from the sorting step.

    The resulting string is the canonicalized query string.

  2. Construct the string to sign.

    Use the percentEncode method to process the canonicalized query string from Step 1 and construct the string to sign according to the following rules:

    StringToSign=
      HTTPMethod + "&" +                      // HTTPMethod: The HTTP method used to send the request, such as GET.
      percentEncode("/") + "&" +              // percentEncode("/"): The value obtained by UTF-8 encoding the character (/), which is %2F.
      percentEncode(CanonicalizedQueryString) // Your canonicalized query string.
  3. Calculate the HMAC value.

    Calculate the Hash-based Message Authentication Code (HMAC) for the StringToSign string from Step 2 as defined in RFC2104.

    Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )
    Note

    The key used to calculate the signature is your

    AccessKeySecret

    followed by an ampersand (&) character (ASCII code 38). The hash algorithm is SHA1.

  4. Calculate the signature value.

    Base64-encode the HMAC value from Step 3 to obtain the signature string (Signature).

  5. Add the signature.

    Add the signature string as the Signature parameter to the request. The value of the Signature parameter must be URL-encoded according to RFC3986. This completes the request signing process.

Signature example

This example shows how to call the GetCardFlowInfo API operation. Assume that you have the following values: AccessKeyId=testid, AccessKeySecret=testsecret, ProductKey=12345abcde, TopicFullName=/12345abcde/testdevice/user/get, and MessageContent=aGVsbG8gd29ybGQ.

  1. Construct the request URL before signing.

    http://linkcard.aliyuncs.com/?Action=GetCardFlowInfo&MessageContent=aGVsbG8gd29ybGQ&Timestamp=2021-12-24T11:43:57Z&SignatureVersion=1.0&ServiceCode=iot&Format=XML&Qos=0&SignatureNonce=432101234567&Version=2018-01-20&AccessKeyId=testid&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcde&TopicFullName=/12345abcde/testdevice/user/get
  2. Calculate the string to sign, `StringToSign`.

    GET&%2F&AccessKeyId%3Dtestid%26Action%3DPub%26Format%3DXML%26MessageContent%3DaGVsbG8gd29ybGQ%26ProductKey%3D12345abcde%26Qos%3D0%26RegionId%3Dcn-shanghai%26ServiceCode%3Diot%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D432101234567%26SignatureVersion%3D1.0%26Timestamp%3D2018-07-31T07%253A43%253A57Z%26TopicFullName%3D%2F12345abcde%2Ftestdevice%2Fuser%2Fget%26Version%3D2018-01-20
  3. Calculate the signature.

    Because AccessKeySecret=testsecret, the key used for the calculation is testsecret&. The calculated signature is:

    z8Mf/mjEvaLCBrwkev9S3rvsjJs=
  4. Add the signature to the request URL as the Signature parameter. The final URL is:

    https://linkcard.aliyuncs.com/?MessageContent=aGVsbG8gd29ybGQ&Action=GetCardFlowInfo&Timestamp=2021-12-24T11%503A43%253A57Z&SignatureVersion=1.0&ServiceCode=linkcard&Format=XML&Qos=0&SignatureNonce=432101234567&Version=2021-05-20&AccessKeyId=testid&Signature=%2BWylkkU*************T0NM%3D&SignatureMethod=HMAC-SHA1&RegionId=cn-shanghai&ProductKey=12345abcde&TopicFullName=%2F12345abcde%2Ftestdevice%2Fuser%2Fget

Java code example

The following Java code provides an example of how to generate a signature.

  1. The configuration file Config.java.

    /*   
     * Copyright © 2018 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.sign;
    /**
     * Server-side API signature configuration file
     * 
     * @author: ali
     * @version: 0.1 2018-08-08 08:23:54
     */
    public class Config {
        // AccessKey information
        public static String accessKey = "1234567890123456";
        public static String accessKeySecret = "123456789012345678901234567890";
        public final static String CHARSET_UTF8 = "utf8";
    }

    Parameter

    Example

    Description

    accessKey

    1234567890123456

    Log on to the Alibaba Cloud Management Console. Move the mouse pointer over your profile picture and click AccessKey Management to obtain the AccessKey ID and AccessKey secret.

    Note

    If you use a Resource Access Management (RAM) user, you must grant the RAM user the permission to manage IoT Mobile Connection Package (AliyunLinkCardFullAccess). Otherwise, the connection fails.

    accessKeySecret

    123456789012345678901234567890

  2. The utility file UrlUtil.java.

    /*   
     * Copyright © 2018 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.sign;
    import java.net.URLEncoder;
    import java.util.Map;
    import org.apache.commons.lang3.StringUtils;
    /**
     * URL processing class
     * 
     * @author: ali
     * @version: 0.1 2018-06-21 20:40:52
     */
    public class UrlUtil {
        private final static String CHARSET_UTF8 = "utf8";
        public static String urlEncode(String url) {
            if (!StringUtils.isEmpty(url)) {
                try {
                    url = URLEncoder.encode(url, "UTF-8");
                } catch (Exception e) {
                    System.out.println("Url encode error:" + e.getMessage());
                }
            }
            return url;
        }
        public static String generateQueryString(Map<String, String> params, boolean isEncodeKV) {
            StringBuilder canonicalizedQueryString = new StringBuilder();
            for (Map.Entry<String, String> entry : params.entrySet()) {
                if (isEncodeKV)
                    canonicalizedQueryString.append(percentEncode(entry.getKey())).append("=")
                            .append(percentEncode(entry.getValue())).append("&");
                else
                    canonicalizedQueryString.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
            }
            if (canonicalizedQueryString.length() > 1) {
                canonicalizedQueryString.setLength(canonicalizedQueryString.length() - 1);
            }
            return canonicalizedQueryString.toString();
        }
        public static String percentEncode(String value) {
            try {
                // After encoding with URLEncoder.encode, replace "+", "*", and "%7E" to meet the API encoding specifications.
                return value == null ? null
                        : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
                                "~");
            } catch (Exception e) {
            }
            return "";
        }
    }
  3. The utility file SignatureUtils.java.

    /*   
     * Copyright © 2018 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.sign;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.net.URI;
    import java.net.URISyntaxException;
    import java.net.URLDecoder;
    import java.net.URLEncoder;
    import java.util.Map;
    import java.util.TreeMap;
    import javax.crypto.Mac;
    import javax.crypto.spec.SecretKeySpec;
    import org.apache.commons.codec.binary.Base64;
    import org.apache.commons.lang3.StringUtils;
    /**
     * Server-side API signature
     * 
     * @author: ali
     * @version: 0.1 2018-06-21 20:47:05
     */
    public class SignatureUtils {
        private final static String CHARSET_UTF8 = "utf8";
        private final static String ALGORITHM = "HmacSHA1";
        private final static String SEPARATOR = "&";
        public static Map<String, String> splitQueryString(String url)
                throws URISyntaxException, UnsupportedEncodingException {
            URI uri = new URI(url);
            String query = uri.getQuery();
            final String[] pairs = query.split("&");
            TreeMap<String, String> queryMap = new TreeMap<String, String>();
            for (String pair : pairs) {
                final int idx = pair.indexOf("=");
                final String key = idx > 0 ? pair.substring(0, idx) : pair;
                if (!queryMap.containsKey(key)) {
                    queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8));
                }
            }
            return queryMap;
        }
        public static String generate(String method, Map<String, String> parameter, String accessKeySecret)
                throws Exception {
            String signString = generateSignString(method, parameter);
            System.out.println("signString---" + signString);
            byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString);
            String signature = newStringByBase64(signBytes);
            System.out.println("signature----" + signature);
            if ("POST".equals(method))
                return signature;
            return URLEncoder.encode(signature, "UTF-8");
        }
        public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException {
            TreeMap<String, String> sortParameter = new TreeMap<String, String>();
            sortParameter.putAll(parameter);
            String canonicalizedQueryString = UrlUtil.generateQueryString(sortParameter, true);
            if (null == httpMethod) {
                throw new RuntimeException("httpMethod can not be empty");
            }
            StringBuilder stringToSign = new StringBuilder();
            stringToSign.append(httpMethod).append(SEPARATOR);
            stringToSign.append(percentEncode("/")).append(SEPARATOR);
            stringToSign.append(percentEncode(canonicalizedQueryString));
            return stringToSign.toString();
        }
        public static String percentEncode(String value) {
            try {
                return value == null ? null
                        : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
                                "~");
            } catch (Exception e) {
            }
            return "";
        }
        public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
            if (StringUtils.isEmpty(secret)) {
                throw new IOException("secret can not be empty");
            }
            if (StringUtils.isEmpty(baseString)) {
                return null;
            }
            Mac mac = Mac.getInstance("HmacSHA1");
            SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
            mac.init(keySpec);
            return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
        }
        public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8);
        }
    }
  4. The main entry file Main.java.

    /*   
     * Copyright © 2018 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.sign;
    import java.io.UnsupportedEncodingException;
    import java.net.URLEncoder;
    import java.util.HashMap;
    import java.util.Map;
    /**
     * Main entry point for the signature tool
     * 
     * @author: ali
     * @version: 0.1 2018-09-18 15:06:48
     */
    public class Main {
        // 1. Modify the AccessKey information in Config.java.
        // 2. Use method 2. All parameters must be filled in.
        // 3. The "Final signature" is the final signature result that you need.
        public static void main(String[] args) throws UnsupportedEncodingException {
            // Method 1
            System.out.println("Method 1:");
            String str = "GET&%2F&AccessKeyId%3D" + Config.accessKey
                    + "%26Action%3DRegisterDevice%26DeviceName%3D1533023037%26Format%3DJSON%26ProductKey%3DaxxxUtgaRLB%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D1533023037%26SignatureVersion%3D1.0%26Timestamp%3D2018-07-31T07%253A43%253A57Z%26Version%3D2018-01-20";
            byte[] signBytes;
            try {
                signBytes = SignatureUtils.hmacSHA1Signature(Config.accessKeySecret + "&", str.toString());
                String signature = SignatureUtils.newStringByBase64(signBytes);
                System.out.println("signString---" + str);
                System.out.println("signature----" + signature);
                System.out.println("Final signature: " + URLEncoder.encode(signature, Config.CHARSET_UTF8));
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println();
            // Method 2
            System.out.println("Method 2:");
            Map<String, String> map = new HashMap<String, String>();
            // Common parameters
            map.put("Format", "JSON");
            map.put("Version", "2018-01-20");
            map.put("AccessKeyId", Config.accessKey);
            map.put("SignatureMethod", "HMAC-SHA1");
            map.put("Timestamp", "2018-07-31T07:43:57Z");
            map.put("SignatureVersion", "1.0");
            map.put("SignatureNonce", "1533023037");
            map.put("RegionId", "cn-shanghai");
            // Request parameters
            map.put("Action", "RegisterDevice");
            map.put("DeviceName", "1533023037");
            map.put("ProductKey", "a***UtgaRLB");
            try {
                String signature = SignatureUtils.generate("GET", map, Config.accessKeySecret);
                System.out.println("Final signature: " + signature);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println();
        }
    }