MQTT 経由で一機一元動的登録を実行し、DeviceSecret を取得して IoT Platform 認証を行う方法を示す Java サンプルです。
前提条件
製品ごとの一意の証明書検証トピックで説明されている次の手順を実行します。
プロダクトを作成します。
動的登録を有効にします。
デバイスを追加します。
デバイスにデバイス証明書をインストールします。
背景情報
IoT Platformは、デバイスの複数の認証方法をサポートしています。 詳細については、「デバイスの認証」をご参照ください。
IoT Platform は、MQTT 経由の動的登録をサポートしており、一機一元方式を使用した事前登録デバイスと登録不要デバイスの両方に対応しています。プロセスとパラメーターの詳細については、「MQTT経由のデバイス動的登録」をご参照ください。
開発環境の準備
開発環境:
-
オペレーティングシステム: Windows 10
-
JDK: JDK 8
操作手順
- IntelliJ IDEA を開き、Maven プロジェクトを作成します。この例では、プロジェクト名を [MqttDynamicRegistration] とします。
- プロジェクトの pom.xml ファイルに、以下の Maven 依存関係を追加し、[Load Maven Changes] アイコンをクリックしてダウンロードします。
<dependency> <groupId>org.eclipse.paho</groupId> <artifactId>org.eclipse.paho.client.mqttv3</artifactId> <version>1.2.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency> - [MqttDynamicRegistration] プロジェクトの /src/main/java ディレクトリに、
DynamicRegisterByMqttという名前の Java クラスを作成し、以下のコードを追加します。説明デバイスがアクティベートされていない場合、動的登録を複数回実行できます。最新の登録で取得したDeviceSecretが有効になります。最新のDeviceSecretをデバイスに永続化してください。デバイスが既にアクティベートされている場合、デバイスを再登録する前に、ResetThing API を呼び出して、クラウド上の動的登録ステータスを未登録にリセットする必要があります。
import java.nio.charset.StandardCharsets; import java.util.Random; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; import org.eclipse.paho.client.mqttv3.MqttCallback; import org.eclipse.paho.client.mqttv3.MqttClient; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttMessage; import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; import com.alibaba.fastjson.JSONObject; /** * デバイスの動的登録を実行します。 */ public class DynamicRegisterByMqtt { // プロダクトが所在するリージョン ID。 private static String regionId = "cn-shanghai"; // 暗号化方式。使用可能な MAC アルゴリズムは HmacMD5、HmacSHA1、HmacSHA256 です。値は signmethod パラメーターと一致している必要があります。 private static final String HMAC_ALGORITHM = "hmacsha1"; // IoT Platform からデバイス証明書を受信するトピック。このトピックは作成やサブスクライブせずに直接使用できます。 private static final String REGISTER_TOPIC = "/ext/register"; /** * 動的登録を実行します。 * * @param productKey プロダクトの ProductKey。 * @param productSecret プロダクトの ProductSecret。 * @param deviceName デバイスの名前。 * @throws Exception */ public void register(String productKey, String productSecret, String deviceName) throws Exception { // エンドポイント。Transport Layer Security (TLS) を使用する必要があります。 String broker = "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883"; // クライアント ID。デバイスの MAC アドレスまたはシリアル番号 (SN) を使用することを推奨します。クライアント ID は 64 文字以内である必要があります。 String clientId = productKey + "." + deviceName; // ランダム値を取得します。 Random r = new Random(); int random = r.nextInt(1000000); // securemode パラメーターは 2 にのみ設定でき、TLS のみを使用できることを示します。signmethod パラメーターは署名アルゴリズムを指定します。 String clientOpts = "|securemode=2,authType=register,signmethod=" + HMAC_ALGORITHM + ",random=" + random + "|"; // 接続用の MQTT クライアント ID。 String mqttClientId = clientId + clientOpts; // 接続用の MQTT ユーザー名。 String mqttUsername = deviceName + "&" + productKey; // 接続用の MQTT パスワード (署名)。 JSONObject params = new JSONObject(); params.put("productKey", productKey); params.put("deviceName", deviceName); params.put("random", random); String mqttPassword = sign(params, productSecret); // MQTT CONNECT メッセージを使用して動的登録を実行します。 connect(broker, mqttClientId, mqttUsername, mqttPassword); } /** * MQTT CONNECT メッセージを使用して動的登録情報を送信します。 * * @param serverURL 動的登録用のエンドポイント。 * @param clientId クライアント ID。 * @param username MQTT ユーザー名。 * @param password MQTT パスワード。 */ @SuppressWarnings("resource") private void connect(String serverURL, String clientId, String username, String password) { try { MemoryPersistence persistence = new MemoryPersistence(); MqttClient sampleClient = new MqttClient(serverURL, clientId, persistence); MqttConnectOptions connOpts = new MqttConnectOptions(); connOpts.setMqttVersion(4); // MQTT 3.1.1 を使用します。 connOpts.setUserName(username); // ユーザー名。 connOpts.setPassword(password.toCharArray()); // パスワード。 connOpts.setAutomaticReconnect(false); // MQTT 動的登録プロトコルでは、自動再接続を無効にする必要があります。 System.out.println("----- register params -----"); System.out.print("server=" + serverURL + ",clientId=" + clientId); System.out.println(",username=" + username + ",password=" + password); sampleClient.setCallback(new MqttCallback() { @Override public void messageArrived(String topic, MqttMessage message) throws Exception { // 動的登録のレスポンスのみを処理します。 if (REGISTER_TOPIC.equals(topic)) { String payload = new String(message.getPayload(), StandardCharsets.UTF_8); System.out.println("----- register result -----"); System.out.println(payload); sampleClient.disconnect(); } } @Override public void deliveryComplete(IMqttDeliveryToken token) { } @Override public void connectionLost(Throwable cause) { } }); sampleClient.connect(connOpts); } catch (MqttException e) { System.out.print("register failed: clientId=" + clientId); System.out.println(",username=" + username + ",password=" + password); System.out.println("reason " + e.getReasonCode()); System.out.println("msg " + e.getMessage()); System.out.println("loc " + e.getLocalizedMessage()); System.out.println("cause " + e.getCause()); System.out.println("except " + e); e.printStackTrace(); } } /** * 動的登録用の署名を生成します。 * * @param params 署名に使用するパラメーター。 * @param productSecret プロダクトの ProductSecret。 * @return 16 進数文字列としての署名。 */ private String sign(JSONObject params, String productSecret) { // リクエストパラメーターをアルファベット順にソートします。 Set<String> keys = getSortedKeys(params); // 'sign' および 'signMethod' パラメーターを除外します。 keys.remove("sign"); keys.remove("signMethod"); // 署名用のプレーンテキストを組み立てます。 StringBuffer content = new StringBuffer(); for (String key : keys) { content.append(key); content.append(params.getString(key)); } // 署名を計算します。 String sign = encrypt(content.toString(), productSecret); System.out.println("sign content=" + content); System.out.println("sign result=" + sign); return sign; } /** * JSON オブジェクトからソート済みのキーセットを取得します。 * * @param json ソートする JSON オブジェクト。 * @return ソート済みのキーセット。 */ private Set<String> getSortedKeys(JSONObject json) { SortedMap<String, String> map = new TreeMap<String, String>(); for (String key : json.keySet()) { String value = json.getString(key); map.put(key, value); } return map.keySet(); } /** * HMAC_ALGORITHM を使用してコンテンツを暗号化します。 * * @param content プレーンテキスト。 * @param secret キー。 * @return 暗号文。 */ private String encrypt(String content, String secret) { try { byte[] text = content.getBytes(StandardCharsets.UTF_8); byte[] key = secret.getBytes(StandardCharsets.UTF_8); SecretKeySpec secretKey = new SecretKeySpec(key, HMAC_ALGORITHM); Mac mac = Mac.getInstance(secretKey.getAlgorithm()); mac.init(secretKey); return byte2hex(mac.doFinal(text)); } catch (Exception e) { e.printStackTrace(); return null; } } /** * バイト配列を 16 進数文字列に変換します。 * * @param b バイト配列。 * @return 16 進数文字列。 */ private String byte2hex(byte[] b) { StringBuffer sb = new StringBuffer(); for (int n = 0; b != null && n < b.length; n++) { String stmp = Integer.toHexString(b[n] & 0XFF); if (stmp.length() == 1) { sb.append('0'); } sb.append(stmp); } return sb.toString().toUpperCase(); } public static void main(String[] args) throws Exception { String productKey = "a1IoK******"; String productSecret = "6vEu5Qlj5S******"; String deviceName = "OvenDevice01"; // 動的登録を実行します。 DynamicRegisterByMqtt client = new DynamicRegisterByMqtt(); client.register(productKey, productSecret, deviceName); // 動的登録後、DeviceSecret をデバイス上でローカルに永続化する必要があります。 } } - 実際の
デバイス情報を使用して、コード内のパラメーターを設定します。パラメーター 例 説明 regionId cn-shanghai IoT Platformインスタンスのリージョン ID。リージョンコードのリストについては、「リージョンリスト」をご参照ください。productKey a1IoK****** プロダクトのProductKey。デバイスに書き込まれています。IoT Platformコンソールにログインし、[プロダクトの詳細] ページでProductKeyを確認できます。productSecret 6vEu5Qlj5S****** プロダクトのProductSecret。デバイスに書き込まれています。IoT Platformコンソールにログインし、[プロダクトの詳細] ページでProductSecretを確認できます。deviceName OvenDevice01 デバイスの名前。IoT Platformは、デバイスのアクティベーション時にDeviceNameを検証します。デバイスから直接読み取り可能な識別子 (MAC アドレス、IMEI、シリアル番号 (SN) など) をDeviceNameとして使用することを推奨します。broker "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883" デバイスの動的登録用のエンドポイント。形式はssl://" + "${YourInstanceDomain}" + ":" +1883です。この形式では、${YourInstanceDomain} は MQTT
エンドポイントです。MQTTエンドポイントの取得方法については、「インスタンスエンドポイントの表示と設定」をご参照ください。 - DynamicRegisterByMqtt.java ファイルを実行します。
デバイスは、DeviceName、プロダクトのProductKey、およびProductSecretを含む認証リクエストをクラウドに送信します。IoT Platformがリクエストを検証した後、デバイスはクラウドからDeviceSecret(例:8d1f0cdab49dd229cf3b75****) を受信します。sign content=deviceNameOvenDevice01productKeyaIIoxxxrandom462335 sign result=A76A60CEDBA4899D304320A58E8719D75xxx ----- register params ----- server=ssl://aIIoxxx.iot-as-mqtt.cn-shanghai.aliyuncs.com:1883,clientId=aIIoxxx.OvenDevice01|securemode=2,authType=register,signmethod=hmacsha1,random=462335|,username=OvenDevice01&aIIoxxx,password=A76A60CE ----- register result ----- {"deviceSecret":"8d1f0cdab49dd229cf3b75xxx","productKey":"aIIoxxx","deviceName":"OvenDevice01"} Process finished with exit code 0
次のステップ
デバイス が デバイス証明書 (ProductKey、DeviceName、DeviceSecret) を取得した後、MQTT クライアントを使用して IoT Platform に接続し、データ通信を行ってください。
詳細については、「Paho MQTT Java接続例」をご参照ください。