Signature mechanism

更新时间:
复制 MD 格式

IoT Platform authenticates the sender of each API request. Therefore, you must include a signature in all API requests that use the HTTP or HTTPS protocol.

Signature method

To sign a request, go to the AccessKey Management page in the console to obtain the AccessKey ID and AccessKey secret for your Alibaba Cloud account. Then, you can generate a Message Authentication Code (MAC). The AccessKey ID identifies the user. The AccessKey secret is the key used to encrypt the signature string and for the server to verify the signature string. You must keep your AccessKey secret confidential.

Note

IoT Platform provides server-side software development kits (SDKs) for languages such as Java, Python, and PHP. Using these SDKs eliminates the need to manually perform the signing process. For instructions on how to use each SDK, see Server-side SDKs (original) and Server-side SDKs (upgraded).

To sign the request, perform the following steps:

  1. Construct a canonicalized query string from the request parameters.

    1. Sort the parameters.

      Sort the request parameters alphabetically by parameter name. These parameters include common request parameters and the custom parameters for the API. Do not include the Signature parameter itself.

      Note

      When you use the GET method to submit a request, these parameters are the part of the request URL that follows the ? and are separated by &.

    2. URL-encode the name and value of each request parameter.

      Encode the parameter names and values using the UTF-8 character set according to the rules in RFC 3986. The encoding rules are as follows:

      • The characters A to Z, a to z, 0 to 9, and the characters -, _, ., and ~ are not encoded.

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

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

      • A space must be encoded as %20 instead of a plus sign (+).

      This encoding method is similar to, but not the same as, the encoding algorithm for the application/x-www-form-urlencoded Multipurpose Internet Mail Extensions (MIME) type.

      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 the plus sign (+) with %20, the asterisk (*) with %2A, and %7E with a tilde (~). This produces an encoded string that complies with the preceding 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. Connect the encoded parameter name and its value with an equal sign (=).

    4. Sort the parameter-value pairs alphabetically by parameter name, and then concatenate them using the & symbol.

    After you complete these steps, you obtain the canonicalized query string.

  2. Construct the string to sign.

    Create the string-to-sign by processing the canonicalized query string from Step 1 with percentEncode. The string must be in the following format:

    StringToSign=
      HTTPMethod + "&" +                      
      percentEncode("/") + "&" +              
      percentEncode(CanonicalizedQueryString)

    Parameter description:

    • HTTPMethod: The HTTP method used to send the request, such as GET.

    • percentEncode("/"): The UTF-8 encoded value of the forward slash (/), which is "%2F".

    • percentEncode(CanonicalizedQueryString): The percent-encoded canonicalized query string.

  3. Calculate the HMAC value.

    Use the StringToSign string that you created in Step 2 to calculate the HMAC value as defined in RFC 2104.

    HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) )
    Important

    The key for the signature calculation is your AccessKeySecret followed by an ampersand (&) character (ASCII code 38). The hash algorithm is SHA1.

  4. Calculate the signature value.

    Encode the HMAC value from Step 3 into a string using Base64 encoding rules. The resulting string is the signature value (Signature).

    Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )
  5. Add the sign.

    Add the Signature parameter to the request parameters. The value of this parameter is the resulting signature, which must be URL-encoded as specified in RFC 3986.

Signature example

This example shows how to call the Pub API. For this example, assume the following parameter values: AccessKeyId=testid, AccessKeySecret=testsecret, ProductKey=12345abcde, TopicFullName=/12345abcde/testdevice/user/get, MessageContent=aGVsbG8gd29ybGQ, and Qos=0.

  1. Construct the request URL before signing.

    http://iot.cn-shanghai.aliyuncs.com/?Action=Pub&MessageContent=aGVsbG8gd29ybGQ&Timestamp=2018-07-31T07:43:57Z&SignatureVersion=1.0&Format=XML&Qos=0&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&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%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf%26SignatureVersion%3D1.0%26Timestamp%3D2018-07-31T07%253A43%253A57Z%26TopicFullName%3D%252F12345abcde%252Ftestdevice%252Fuser%252Fget%26Version%3D2018-01-20
  3. Calculate the signature value.

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

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

    http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG8gd29ybGQ&Action=Pub&Timestamp=2018-07-31T07%253A43%253A57Z&SignatureVersion=1.0&Format=XML&Qos=0&SignatureNonce=3ee8c1b8-83d3-44af-a94f-4e0ad82fd6cf&Version=2018-01-20&AccessKeyId=testid&Signature=NUh3otvAoXOZmG%2Fa2gDShh6Ze9w%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 create a signature.

  1. Add Maven project dependencies.

    <!-- Apache Commons Lang 3.x -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version> <!-- Use the latest version -->
    </dependency>
    
    <!-- Apache Commons Codec -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.15</version> <!-- Use the latest version -->
    </dependency>
  2. The configuration file is Config.java.

    /*   
     * Copyright  2018 Alibaba. All rights reserved.
     */
    package com.aliyun.iot.demo.sign;
    
    /**
     * Configuration file for server-side API signing
     * 
     * @author: ali
     * @version: 0.1 2018-08-08 08:23:54
     */
    public class Config {
    
        // AccessKey information
        public static String accessKey = "123456******3456";
        public static String accessKeySecret = "123456******34567******4567890";
    
        public final static String CHARSET_UTF8 = "utf8";
    }

    Parameter

    Example

    Description

    accessKey

    123456******3456

    Log on to the IoT Platform console. Move the 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 permissions to manage IoT Platform (AliyunIOTFullAccess). Otherwise, the connection fails. For information about how to grant permissions, see Grant a RAM user permissions to access IoT Platform.

    accessKeySecret

    123456******34567******4567890

  3. The configuration file is 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 "";
        }
    }
  4. The configuration file is 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 signing
     * 
     * @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);
        }
    }
  5. Create 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 for the signing tool
     * 
     * @author: ali
     * @version: 0.1 2018-09-18 15:06:48
     */
    public class Main {
    
        // 1. Modify the AccessKey information in Config.java.
        // 2. Method 2 is recommended. All parameters must be specified.
        // 3. "Final signature" is the final signature result.
        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", "axxxUtgaRLB");
            try {
                String signature = SignatureUtils.generate("GET", map, Config.accessKeySecret);
                System.out.println("Final signature: " + signature);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println();
        }
    }