Signature mechanism
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:
Construct a canonicalized query string.
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.
NoteFor GET requests, these parameters are the part of the request URL that follows the question mark (
?) and are connected by ampersands (&).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
%XYformat.XYrepresents 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
%20instead of plus signs (+).
This encoding method is similar to, but different from, the
application/x-www-form-urlencodedMultipurpose Internet Mail Extensions (MIME) format encoding algorithm.If you use
java.net.URLEncoderfrom the Java standard library, you can first encode the string using thepercentEncodemethod. Then, in the encoded string, replace plus signs (+) with%20, asterisks (*) with%2A, and%7Ewith 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; }Use an equal sign (
=) to connect the encoded parameter name and value.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.
Construct the string to sign.
Use the
percentEncodemethod 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.Calculate the HMAC value.
Calculate the Hash-based Message Authentication Code (HMAC) for the
StringToSignstring from Step 2 as defined in RFC2104.Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) )NoteThe key used to calculate the signature is your
AccessKeySecret followed by an ampersand (
&) character (ASCII code 38). The hash algorithm is SHA1.Calculate the signature value.
Base64-encode the HMAC value from Step 3 to obtain the signature string (Signature).
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.
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/getCalculate 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-20Calculate the signature.
Because
AccessKeySecret=testsecret, the key used for the calculation is testsecret&. The calculated signature is:z8Mf/mjEvaLCBrwkev9S3rvsjJs=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.
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.
NoteIf 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
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 ""; } }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); } }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(); } }