IoT Platform authentifie l'expéditeur de chaque requête API. Vous devez donc inclure une signature dans toutes les requêtes API utilisant le protocole HTTP ou HTTPS.
Méthode de signature
Pour signer une requête, accédez à la page AccessKey Management de la console afin d'obtenir l'AccessKey ID et l'AccessKey Secret de votre compte Alibaba Cloud. Générez ensuite un code d'authentification de message (MAC). L'AccessKey ID identifie l'utilisateur. L'AccessKey Secret est la clé servant à chiffrer la chaîne de signature et permettant au serveur de la vérifier. Gardez votre AccessKey Secret confidentiel.
IoT Platform propose des kits de développement logiciel (SDK) côté serveur pour des langages tels que Java, Python et PHP. L'utilisation de ces SDK vous dispense de signer manuellement les requêtes. Pour savoir comment utiliser chaque SDK, consultez les rubriques Server-side SDKs (original) et Server-side SDKs (upgraded).
Pour signer la requête, suivez les étapes ci-dessous :
-
Construisez une chaîne de requête canonique à partir des paramètres de la requête.
-
Triez les paramètres.
Classez les paramètres de requête par ordre alphabétique selon leur nom. Ces paramètres incluent les common request parameters ainsi que les paramètres personnalisés de l'API. N'incluez pas le paramètre Signature lui-même.
RemarqueLorsque vous utilisez la méthode GET pour soumettre une requête, ces paramètres correspondent à la partie de l'URL de requête qui suit le
?et sont séparés par le caractère&. -
Encodez en URL le nom et la valeur de chaque paramètre de requête.
Encodez les noms et les valeurs des paramètres en utilisant le jeu de caractères UTF-8 conformément aux règles définies dans la norme RFC 3986. Les règles d'encodage sont les suivantes :
Les caractères A à Z, a à z, 0 à 9, ainsi que les caractères
-,_,.et~ne sont pas encodés.Les autres caractères sont encodés au format
%XY, oùXYreprésente la valeur hexadécimale du code ASCII du caractère. Par exemple, un guillemet double (") est encodé sous la forme%22.Les caractères UTF-8 étendus sont encodés au format
%XY%ZA….Un espace doit être encodé sous la forme
%20et non sous la forme d'un signe plus (+).
Cette méthode d'encodage est similaire, mais non identique, à l'algorithme d'encodage du type MIME
application/x-www-form-urlencoded.Si vous utilisez
java.net.URLEncoderde la bibliothèque standard Java, encodez d'abord la chaîne à l'aide de la méthodepercentEncode. Ensuite, dans la chaîne encodée, remplacez le signe plus (+) par%20, l'astérisque (*) par%2A, et%7Epar un tilde (~). Vous obtenez ainsi une chaîne encodée conforme aux règles précédentes.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; } Reliez le nom du paramètre encodé et sa valeur à l'aide d'un signe égal (=).
Triez les paires paramètre-valeur par ordre alphabétique selon le nom du paramètre, puis concaténez-les en utilisant le symbole
&.
Une fois ces étapes terminées, vous obtenez la chaîne de requête canonique.
-
-
Construisez la chaîne à signer.
Créez la chaîne à signer en traitant la chaîne de requête canonique de l'étape 1 avec
percentEncode. La chaîne doit respecter le format suivant :StringToSign= HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString)Description des paramètres :
HTTPMethod : la méthode HTTP utilisée pour envoyer la requête, telle que GET.
percentEncode("/") : la valeur encodée en UTF-8 de la barre oblique (/), soit « %2F ».
percentEncode(CanonicalizedQueryString) : la chaîne de requête canonique encodée en pourcentage.
-
Calculez la valeur HMAC.
Utilisez la chaîne
StringToSigncréée à l'étape 2 pour calculer la valeur HMAC telle que définie dans la norme RFC 2104.HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) )ImportantLa clé utilisée pour le calcul de la signature est votre AccessKeySecret suivi d'un caractère esperluette (
&) (code ASCII 38). L'algorithme de hachage est SHA1. -
Calculez la valeur de signature.
Encodez la valeur HMAC de l'étape 3 en une chaîne à l'aide des règles d'encodage Base64. La chaîne résultante constitue la valeur de signature (Signature).
Signature = Base64( HMAC-SHA1( AccessSecret, UTF-8-Encoding-Of(StringToSign) ) ) -
Ajoutez la signature.
Ajoutez le paramètre Signature aux paramètres de la requête. La valeur de ce paramètre est la signature résultante, qui doit être encodée en URL conformément à la norme RFC 3986.
Exemple de signature
Cet exemple illustre l'appel de l'API Pub. Supposons les valeurs de paramètres suivantes : AccessKeyId=testid, AccessKeySecret=testsecret, ProductKey=12345abcde, TopicFullName=/12345abcde/testdevice/user/get, MessageContent=aGVsbG8gd29ybGQ et Qos=0.
-
Construisez l'URL de requête avant la signature.
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 -
Calculez la chaîne à signer, 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 -
Calculez la valeur de signature.
Étant donné que l'AccessKeySecret est
testsecret, la clé utilisée pour le calcul est testsecret&. La valeur de signature calculée est :NUh3otvAoXOZmG/a2gDShh6Ze9w= -
Ajoutez la signature à l'URL de requête en tant que paramètre Signature. L'URL finale est :
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
Exemple de code Java
Le code Java suivant fournit un exemple de création d'une signature.
-
Ajoutez les dépendances du projet Maven.
<!-- 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> -
Le fichier de configuration est 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"; }Paramètre
Exemple
Description
accessKey
1234563456
Connectez-vous à la console IoT Platform. Placez le pointeur sur votre photo de profil et cliquez sur AccessKey Management pour obtenir l'AccessKey ID et l'AccessKey Secret.
RemarqueSi vous utilisez un utilisateur Resource Access Management (RAM), vous devez accorder à cet utilisateur les autorisations de gestion d'IoT Platform (AliyunIOTFullAccess). Sinon, la connexion échouera. Pour savoir comment accorder des autorisations, consultez la rubrique Accorder à un utilisateur RAM des autorisations d'accès à IoT Platform.
accessKeySecret
123456345674567890
-
Le fichier de configuration est 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 ""; } } -
Le fichier de configuration est 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); } } -
Créez le fichier principal 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(); } }