Unique-certificate-per-product dynamic registration over MQTT
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:
-
Create a product.
-
Enable dynamic registration.
-
(With pre-registration only) Add a device.
-
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:
-
Operating system: Windows 10
-
JDK: JDK 8
Procedure
- Open IntelliJ IDEA and create a Maven project. For this example, name the project MqttDynamicRegistration.
- 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> - In the /src/main/java directory of the MqttDynamicRegistration project, create a Java class named
DynamicRegisterByMqttand add the following code.Note- If a
deviceis not activated, you can performdynamic registrationmultiple times. TheDeviceSecretfrom the most recent registration takes effect. Ensure that the latestDeviceSecretis persisted to thedevice. - If a
deviceis already activated, you must call the ResetThing API operation to reset itsdynamic registrationstatus to unregistered in the cloud before you can re-register thedevice.
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. } } - If a
- Configure the parameters in the code with your actual
deviceinformation.Parameter Example Description regionId cn-shanghai The region IDof yourIoT Platforminstance. For a list of region codes, see Region list.productKey a1IoK****** The ProductKeyof theproduct, which has been burned to thedevice. You can log in to theIoT Platformconsoleand view theProductKeyon the Product Details page.productSecret 6vEu5Qlj5S****** The ProductSecretof theproduct, which has been burned to thedevice. You can log in to theIoT Platformconsoleand view theProductSecreton the Product Details page.deviceName OvenDevice01 The name of your device.IoT Platformverifies theDeviceNameduringdeviceactivation. We recommend that you use an identifier that can be directly read from thedevice, such as its MAC address, International Mobile Equipment Identity (IMEI), or serial number (SN), as theDeviceName.broker "ssl://" + productKey + ".iot-as-mqtt." + regionId + ".aliyuncs.com:1883" The endpointfordevicedynamic registration. The format isssl://" + "${YourInstanceDomain}" + ":" +1883.In this format, ${YourInstanceDomain} is the MQTT
endpoint. For information about how to obtain the MQTTendpoint, see View and configure instance endpoints. - Run the DynamicRegisterByMqtt.java file. The
devicesends an authentication request to the cloud containing theDeviceName, theProductKeyof itsproduct, and theProductSecret.After
IoT Platformverifies the request, thedevicereceives theDeviceSecret(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.