This topic describes how to dynamically register devices that use the Message Queuing Telemetry Transport (MQTT) protocol and obtain the DeviceSecrets. The DeviceSecrets are required for authentication when you connect the devices to IoT Platform. In this topic, sample Java code is used for pre-registration unique-certificate-per-product authentication.

Prerequisites

The following steps that are described in the Unique-certificate-per-product verification topic are performed:

  1. Create a product.
  2. Enable dynamic registration.
  3. Add a device.
  4. Install the device certificates on the devices.

Background information

IoT Platform supports multiple authentication methods for devices. For more information, see Authenticate a device.

You can establish MQTT connections to perform pre-registration unique-certificate-per-product authentication or pre-registration-free unique-certificate-per-product authentication. For more information about the procedure and parameters of MQTT-based dynamic registration, see MQTT-based dynamic registration.

Prepare the development environment

In this example, the development environment consists of the following components:

Procedure

  1. Open IntelliJ IDEA and create a Maven project. In this example, the MQTT dynamic registration project is created.
  2. In the pom.xml file, add the following Maven dependencies and click Load Maven Changes to download the packages.
    <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. Find the MQTT dynamic registration project and create a Java class in the \src\main\java directory of the project. In this example, the DynamicRegisterByMqtt class is created. Enter the following code.
    Note
    • If the device is not activated, you can perform dynamic registration multiple times. However, only the latest DeviceSecret is valid. Make sure that the latest DeviceSecret is burned on the device.
    • If the device is activated, you must call the ResetThing operation to reset the registration status of the device to unregistered in the IoT Platform console. Then, you can dynamically 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;
    
    /**
     * Perform dynamic registration for a device.  
     */
    public class DynamicRegisterByMqtt {
    
        // The ID of the region where your product resides. 
        private static String regionId = "cn-shanghai";
    
        // Specify an encryption algorithm. Valid values: hmacmd5, hmacsha1, and hmacsha256. The value that you specify must be the same as the value of the signmethod parameter. 
        private static final String HMAC_ALGORITHM = "hmacsha1";
    
        // The topic that receives device certificates from IoT Platform. You can directly use the topic without the need to create or subscribe to the topic. 
        private static final String REGISTER_TOPIC = "/ext/register";
    
        /**
         * Dynamic registration. 
         * 
         * @ param productKey: the ProductKey of the product to which the device belongs.
         * @param productSecret: the ProductSecret of the product to which the device belongs.
         * @param deviceName: the DeviceName of the device.
         * @throws Exception
         */
        public void register(String productKey, String productSecret, String deviceName) throws Exception {
    
            // The endpoint of the authentication service. You must use the Transport Layer Security (TLS) protocol. 
            String broker = "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883";
    
            // The client ID. We recommend that you use the media access control (MAC) address or serial number (SN) of the device. The client ID can be a maximum of 64 characters in length. 
            String clientId = productKey + "." + deviceName;
    
            // Obtain a random value. 
            Random r = new Random();
            int random = r.nextInt(1000000);
    
            // Set the value of the securemode parameter to 2. You cannot change the value. A value of 2 specifies that TLS is used. The signmethod parameter specifies an encryption algorithm. 
            String clientOpts = "|securemode=2,authType=register,signmethod=" + HMAC_ALGORITHM + ",random=" + random + "|";
    
            // The ID of the MQTT client. 
            String mqttClientId = clientId + clientOpts;
    
            // The username of the MQTT client. 
            String mqttUsername = deviceName + "&" + productKey;
    
            // The signature that is used by the MQTT client to connect to IoT Platform. 
            JSONObject params = new JSONObject();
            params.put("productKey", productKey);
            params.put("deviceName", deviceName);
            params.put("random", random);
            String mqttPassword = sign(params, productSecret);
    
            // Send an MQTT CONNECT message for dynamic registration. 
            connect(broker, mqttClientId, mqttUsername, mqttPassword);
        }
    
        /**
         * Send an MQTT CONNECT message for dynamic registration. 
         * 
         * @param serverURL: the endpoint for dynamic registration.
         * @param clientId: the ID of the client.
         * @param username: the username of the MQTT client.
         * @param password: the password of the MQTT client.
         */
        @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);// The username.
                connOpts.setPassword(password.toCharArray());// The password.
                connOpts.setAutomaticReconnect(false); // Disable the automatic reconnection feature based on MQTT rules that are configured for dynamic registration. 
                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 {
                        // Print only the response of 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();
            }
        }
    
        /**
         * Generate a signature for dynamic registration. 
         * 
         * @param params: the required parameters that you can use to generate a signature.
         * @param productSecret: the ProductSecret of the product to which the device belongs.
         * @return: the signature in the hexadecimal format.
         */
        private String sign(JSONObject params, String productSecret) {
    
            // Sort request parameters in alphabetical order. 
            Set<String> keys = getSortedKeys(params);
    
            // Remove the sign and signMethod parameters. 
            keys.remove("sign");
            keys.remove("signMethod");
    
            // Obtain the plaintext of the signature. 
            StringBuffer content = new StringBuffer();
            for (String key : keys) {
                content.append(key);
                content.append(params.getString(key));
            }
    
            // Generate a signature. 
            String sign = encrypt(content.toString(), productSecret);
            System.out.println("sign content=" + content);
            System.out.println("sign result=" + sign);
    
            return sign;
        }
    
        /**
         * Sort the keys of a JSON object in alphabetical order. 
         *
         * @param json: the JSON object whose keys you want to sort.
         * @return: the set of keys that are sorted in alphabetical order.
         */
        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();
        }
    
        /**
         * Specify an encryption algorithm by using the HMAC_ALGORITHM parameter. 
         * 
         * @param content: the plaintext.
         * @param secret: the encryption 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;
            }
        }
    
        /**
         * Convert a binary array to a hexadecimal string. 
         * 
         * @param b: the binary 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);
    
            // If dynamic registration is successful, burn the DeviceSecret to your on-premises device. 
        }
    }
  4. Specify the following parameters. Replace the values in the preceding sample code with your device information.
    Parameter Example Description
    regionId cn-shanghai The ID of the region where IoT Platform runs. For information about region IDs, see Regions.
    productKey a1IoK****** The ProductKey that is burned to the device. You can log on to the IoT Platform console and view the ProductKey on the Product Details page.
    productSecret 6vEu5Qlj5S****** The ProductSecret that is burned to the device. You can log on to the IoT Platform console and view the ProductSecret on the Product Details page.
    deviceName OvenDevice01 The name of the device.

    IoT Platform verifies the DeviceName when a device initiates an activation request. We recommend that you use an identifier that can be obtained from the device as the DeviceName. The identifier can be the MAC address, International Mobile Equipment Identity (IMEI) number, or SN of the device.

    broker "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883" The endpoint that is used in dynamic registration. Format: ssl://" + "${YourInstanceDomain}" + ":" +1883.

    Replace the ${YourInstanceDomain} variable with the MQTT endpoint. For information about how to obtain the MQTT endpoint, see View the endpoint of an instance.

  5. Run the DynamicRegisterByMqtt.java file. This way, the device can send an authentication request to IoT Platform. The DeviceName, ProductKey, and ProductSecret are included in the request.

    The following figure shows the result. After the device passes the authentication, the device receives a DeviceSecret that is issued by IoT Platform. In this example, the DeviceSecret is 8d1f0cdab49dd229cf3b75**********.

    Result

What to do next

After the device obtains the device certificate that contains the ProductKey, DeviceName, and DeviceSecret, you can use the MQTT client to connect the device to IoT Platform for data communication.

For more information, see Use the Paho MQTT Java client.