Unique-certificate-per-product dynamic registration over MQTT

更新时间:
复制 MD 格式

A Java example that demonstrates how to perform unique-certificate-per-product dynamic registration over MQTT to obtain the DeviceSecret for IoT Platform authentication.

Prerequisites

You have completed the following steps described in the unique-certificate-per-product documentation:

  1. Create a product.

  2. Enable dynamic registration.

  3. (With pre-registration only) Add a device.

  4. Flash device information on the production line.

Background information

IoT Platform supports multiple device authentication methods. For more information, see Device authentication.

IoT Platform supports dynamic registration over MQTT for both pre-registered and registration-free devices using the unique-certificate-per-product method. For details about the process and parameters, see Dynamic device registration over MQTT.

Prepare the development environment

Development environment:

Procedure

  1. Open IntelliJ IDEA and create a Maven project. For this example, name the project MqttDynamicRegistration.
  2. In the project's pom.xml file, add the following Maven dependencies, and then click the Load Maven Changes icon to download them.
    <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>
  3. In the /src/main/java directory of the MqttDynamicRegistration project, create a Java class named DynamicRegisterByMqtt and add the following code.
    Note
    • If a device is not activated, you can perform dynamic registration multiple times. The DeviceSecret from the most recent registration takes effect. Ensure that the latest DeviceSecret is persisted to the device.
    • If a device is already activated, you must call the ResetThing API operation to reset its dynamic registration status to unregistered in the cloud before you can re-register the device.
    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;
    /**
     * Performs dynamic registration for a device. 
     */
    public class DynamicRegisterByMqtt {
        // The region ID where your product is located.
        private static String regionId = "cn-shanghai";
        // The encryption method. The available MAC algorithms are HmacMD5, HmacSHA1, and HmacSHA256. The value must be consistent with the signmethod parameter.
        private static final String HMAC_ALGORITHM = "hmacsha1";
        // The topic for receiving the device certificate from IoT Platform. You can use this topic directly without creating or subscribing to it.
        private static final String REGISTER_TOPIC = "/ext/register";
        /**
         * Performs dynamic registration.
         * 
         * @param productKey The ProductKey of the product.
         * @param productSecret The ProductSecret of the product.
         * @param deviceName The name of the device.
         * @throws Exception
         */
        public void register(String productKey, String productSecret, String deviceName) throws Exception {
            // The endpoint. You must use Transport Layer Security (TLS).
            String broker = "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
            // The client ID. We recommend that you use the device's MAC address or serial number (SN). The client ID must be within 64 characters.
            String clientId = productKey + "." + deviceName;
            // Obtains a random value.
            Random r = new Random();
            int random = r.nextInt(1000000);
            // The securemode parameter can only be set to 2, which indicates that only TLS can be used. The signmethod parameter specifies the signature algorithm.
            String clientOpts = "|securemode=2,authType=register,signmethod=" + HMAC_ALGORITHM + ",random=" + random + "|";
            // The MQTT client ID for connection.
            String mqttClientId = clientId + clientOpts;
            // The MQTT username for connection.
            String mqttUsername = deviceName + "&" + productKey;
            // The MQTT password for connection, which is the signature.
            JSONObject params = new JSONObject();
            params.put("productKey", productKey);
            params.put("deviceName", deviceName);
            params.put("random", random);
            String mqttPassword = sign(params, productSecret);
            // Perform dynamic registration through an MQTT CONNECT message.
            connect(broker, mqttClientId, mqttUsername, mqttPassword);
        }
        /**
         * Sends dynamic registration information through an MQTT CONNECT message.
         * 
         * @param serverURL The endpoint for dynamic registration.
         * @param clientId The client ID.
         * @param username The MQTT username.
         * @param password The MQTT password.
         */
        @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); // Use MQTT 3.1.1.
                connOpts.setUserName(username); // The username.
                connOpts.setPassword(password.toCharArray()); // The password.
                connOpts.setAutomaticReconnect(false); // The MQTT dynamic registration protocol requires disabling automatic reconnection.
                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 {
                        // Process only the response for dynamic registration.
                        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();
            }
        }
        /**
         * Generates a signature for dynamic registration.
         * 
         * @param params The parameters used for the signature.
         * @param productSecret The ProductSecret of the product.
         * @return The signature as a hexadecimal string.
         */
        private String sign(JSONObject params, String productSecret) {
            // Sorts the request parameters in alphabetical order.
            Set<String> keys = getSortedKeys(params);
            // Excludes the 'sign' and 'signMethod' parameters.
            keys.remove("sign");
            keys.remove("signMethod");
            // Assembles the plaintext for the signature.
            StringBuffer content = new StringBuffer();
            for (String key : keys) {
                content.append(key);
                content.append(params.getString(key));
            }
            // Calculates the signature.
            String sign = encrypt(content.toString(), productSecret);
            System.out.println("sign content=" + content);
            System.out.println("sign result=" + sign);
            return sign;
        }
        /**
         * Gets the sorted set of keys from a JSON object.
         *
         * @param json The JSON object to sort.
         * @return The sorted set of keys.
         */
        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();
        }
        /**
         * Encrypts content by using the HMAC_ALGORITHM.
         * 
         * @param content The plaintext.
         * @param secret The key.
         * @return The ciphertext.
         */
        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;
            }
        }
        /**
         * Converts a byte array to a hexadecimal string.
         * 
         * @param b The byte array.
         * @return The hexadecimal string.
         */
        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";
            // Perform dynamic registration.
            DynamicRegisterByMqtt client = new DynamicRegisterByMqtt();
            client.register(productKey, productSecret, deviceName);
            // After dynamic registration, the DeviceSecret must be persisted locally on the device.
        }
    }
  4. Configure the parameters in the code with your actual device information.
    Parameter Example Description
    regionId cn-shanghai The region ID of your IoT Platform instance. For a list of region codes, see Region list.
    productKey a1IoK****** The ProductKey of the product, which has been burned to the device. You can log in to the IoT Platform console and view the ProductKey on the Product Details page.
    productSecret 6vEu5Qlj5S****** The ProductSecret of the product, which has been burned to the device. You can log in to the IoT Platform console and view the ProductSecret on the Product Details page.
    deviceName OvenDevice01 The name of your device.

    IoT Platform verifies the DeviceName during device activation. We recommend that you use an identifier that can be directly read from the device, such as its MAC address, International Mobile Equipment Identity (IMEI), or serial number (SN), as the DeviceName.

    broker "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883" The endpoint for device dynamic registration. The format is ssl://" + "${YourInstanceDomain}" + ":" +1883.

    In this format, ${YourInstanceDomain} is the MQTT endpoint. For information about how to obtain the MQTT endpoint, see View and configure instance endpoints.

  5. Run the DynamicRegisterByMqtt.java file. The device sends an authentication request to the cloud containing the DeviceName, the ProductKey of its product, and the ProductSecret.

    After IoT Platform verifies the request, the device receives the DeviceSecret (for example, 8d1f0cdab49dd229cf3b75****) from the cloud.

    sign content=deviceNameOvenDevice01productKeyaIIoxxxrandom462
    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

Next steps

After the device obtains the device certificate (ProductKey, DeviceName, and DeviceSecret), use an MQTT client to connect it to IoT Platform for data communication.

For more information, see Paho MQTT Java connection example.