IoT Platform は、各 API リクエストの送信者を認証します。したがって、HTTP または HTTPS プロトコルを使用するすべての API リクエストに署名を含める必要があります。
署名方法
リクエストに署名するには、コンソールの 「アクセスキー管理」 ページに移動して、Alibaba Cloud アカウントの AccessKey ID と AccessKey Secret を取得します。これらを使用して、メッセージ認証コード (MAC) を生成できます。AccessKey ID はユーザーを識別します。AccessKey Secret は、署名文字列を暗号化し、サーバーが署名文字列を検証するために使用されるキーです。AccessKey Secret は、機密情報として厳重に管理する必要があります。
IoT Platform は、Java、Python、PHP などの言語に対応したサーバーサイドのソフトウェア開発キット (SDK) を提供しています。これらの SDK を使用すると、署名プロセスを手動で実行する必要がなくなります。各 SDK の使用方法については、「サーバーサイド SDK (オリジナル)」および「サーバーサイド SDK (アップグレード)」をご参照ください。
リクエストに署名するには、次の手順を実行します。
リクエストパラメーターから正規化されたクエリ文字列を構築します。
パラメーターをソートします。
リクエストパラメーターをパラメーター名でアルファベット順にソートします。これらのパラメーターには、共通リクエストパラメーターと API のカスタムパラメーターが含まれます。Signature パラメーター自体は含めないでください。
説明GET メソッドを使用してリクエストを送信する場合、これらのパラメーターはリクエスト URL の
?の後に続く部分であり、&で区切られます。各リクエストパラメーターの名前と値を URL エンコードします。
RFC 3986 のルールに従って、UTF-8 文字セットを使用してパラメーターの名前と値をエンコードします。エンコードのルールは次のとおりです。
A~Z、a~z、0~9 の文字、および
-、_、.、~はエンコードされません。その他の文字は
%XY形式でエンコードされます。ここで、XYは文字の ASCII コードの 16 進値を表します。たとえば、二重引用符 (") は%22としてエンコードされます。拡張 UTF-8 文字は
%XY%ZA…形式でエンコードされます。スペースは、プラス記号 (
+) の代わりに%20としてエンコードする必要があります。
このエンコード方法は、
application/x-www-form-urlencodedMIME (Multipurpose Internet Mail Extensions) タイプのエンコードアルゴリズムと似ていますが、同じではありません。Java 標準ライブラリの
java.net.URLEncoderを使用する場合、まずpercentEncodeメソッドを使用して文字列をエンコードできます。次に、エンコードされた文字列内のプラス記号 (+) を%20に、アスタリスク (*) を%2Aに、%7Eをチルダ (~) に置き換えます。これにより、前述のルールに準拠したエンコード済み文字列が生成されます。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; }エンコードされたパラメーター名とその値を等号 (=) で連結します。
パラメーターと値のペアをパラメーター名でアルファベット順にソートし、
&記号を使用して連結します。
これらの手順を完了すると、正規化されたクエリ文字列を取得できます。
署名対象の文字列を構築します。
ステップ 1 の正規化されたクエリ文字列を
percentEncodeで処理して、署名対象の文字列を作成します。文字列は、以下の形式にする必要があります。StringToSign = HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(CanonicalizedQueryString)パラメーターの説明:
HTTPMethod:リクエストの送信に使用される HTTP メソッド (GET など)。
percentEncode("/"):スラッシュ (/) の UTF-8 エンコード値である「%2F」。
percentEncode(CanonicalizedQueryString):パーセントエンコーディングされた正規化済みクエリ文字列。
HMAC 値を計算します。
RFC 2104 で定義されている HMAC-SHA1 アルゴリズムと、ステップ 2 で作成した
StringToSign文字列を使用して、HMAC 値を計算します。HMAC-SHA1(AccessKeySecret + "&", UTF-8-Encoding-Of(StringToSign))重要署名計算のキーは、AccessKeySecret の後ろにアンパサンド (
&) 文字 (ASCII コード 38) を付けたものです。ハッシュアルゴリズムは SHA1 です。署名値を計算します。
Base64 エンコーディングのルールを使用して、ステップ 3 で取得した HMAC 値を文字列にエンコードします。生成された文字列が署名値 (Signature) です。
Signature = Base64(HMAC-SHA1(AccessKeySecret + "&", UTF-8-Encoding-Of(StringToSign)))署名を追加します。
Signature パラメーターをリクエストパラメーターに追加します。このパラメーターの値は、生成された署名です。この署名は、RFC 3986 の規定に従って URL エンコードする必要があります。
署名の例
この例では、Pub API の呼び出し方法を示します。この例では、パラメーター値として AccessKeyId=testid、AccessKeySecret=testsecret、ProductKey=12345abcde、TopicFullName=/12345abcde/testdevice/user/get、MessageContent=aGVsbG8gd29ybGQ、およびQos=0 を仮定します。
署名前のリクエスト URL を構築します。
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署名対象の文字列 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%3A43%3A57Z%26TopicFullName%3D%252F12345abcde%252Ftestdevice%252Fuser%252Fget%26Version%3D2018-01-20署名値を計算します。
AccessKeySecret が
testsecretであるため、計算に使用されるキーはtestsecret& です。計算された署名値は次のとおりです。NUh3otvAoXOZmG/a2gDShh6Ze9w=署名を Signature パラメーターとしてリクエスト URL に追加します。最終的な URL は次のとおりです。
http://iot.cn-shanghai.aliyuncs.com/?MessageContent=aGVsbG8gd29ybGQ&Action=Pub&Timestamp=2018-07-31T07%3A43%3A57Z&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 コード例
次の Java コードは、署名を作成する方法の例です。
Maven プロジェクトの依存関係を追加します。
<!-- Apache Commons Lang 3.x --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> <!-- 最新バージョンを使用 --> </dependency> <!-- Apache Commons Codec --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-codec</artifactId> <version>1.15</version> <!-- 最新バージョンを使用 --> </dependency>Config.java の内容は次のとおりです。
/* * Copyright 2018 Alibaba. All rights reserved. */ package com.aliyun.iot.demo.sign; /** * サーバーサイド API 署名用の設定ファイル * * @author: ali * @version: 0.1 2018-08-08 08:23:54 */ public class Config { // AccessKey 情報 public static String accessKey = "123456******3456"; public static String accessKeySecret = "123456******34567******4567890"; public final static String CHARSET_UTF8 = "UTF-8"; }パラメーター
例
説明
accessKey
123456**3456
IoT Platform コンソールにログインします。プロフィール画像にポインターを合わせ、[アクセスキー管理] をクリックして、AccessKey ID と AccessKey Secret を取得します。
説明Resource Access Management (RAM) ユーザーを使用する場合、RAM ユーザーに IoT Platform (AliyunIOTFullAccess) を管理する権限を付与する必要があります。そうしないと、接続は失敗します。権限を付与する方法の詳細については、「RAM ユーザーへの IoT Platform へのアクセス権限の付与」をご参照ください。
accessKeySecret
123456**34567**4567890
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 処理クラス * * @author: ali * @version: 0.1 2018-06-21 20:40:52 */ public class UrlUtil { private final static String CHARSET_UTF8 = "UTF-8"; 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 { // URLEncoder.encode でエンコードした後、API エンコード仕様を満たすために「+」、「*」、「%7E」を置き換えます。 return value == null ? null : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E", "~"); } catch (Exception e) { } return ""; } }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; /** * サーバーサイド API 署名 * * @author: ali * @version: 0.1 2018-06-21 20:47:05 */ public class SignatureUtils { private final static String CHARSET_UTF8 = "UTF-8"; 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); } }メインエントリファイル 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; /** * 署名ツールのメインエントリ * * @author: ali * @version: 0.1 2018-09-18 15:06:48 */ public class Main { // 1. Config.java の AccessKey 情報を変更します。 // 2. 方法 2 を推奨します。 すべてのパラメーターを指定する必要があります。 // 3. 「Final signature」は最終的な署名結果です。 public static void main(String[] args) throws UnsupportedEncodingException { // 方法 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%3A43%3A57Z%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(); // 方法 2 System.out.println("Method 2:"); Map<String, String> map = new HashMap<String, String>(); // 共通パラメーター 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"); // リクエストパラメーター 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(); } }