This topic describes how to connect a device to IoT Platform by using Constrained Application Protocol (CoAP). In this example, Java code is used.

Background information

CoAP is suitable for low-power and resource-constrained devices, such as NB-IoT devices. For information about how to connect a device to IoT Platform by using CoAP, see Connect devices to IoT Platform over CoAP.

Note You can connect devices to IoT Platform by using CoAP only in the China (Shanghai) region.

When you connect a device to IoT Platform by using CoAP, you must specify the required parameters, generate a signature for the device, and perform other operations. Eclipse Californium-based sample code is used to describe the configuration process. To ensure data security, symmetric encryption is used.

Prepare the development environment

The following Java development environment is ready for use.

Procedure

  1. Open IntelliJ IDEA and create a Maven project. Example: IotCoap-demo.
  2. Add the following dependencies to the pom.xml file in the project, and click Load Maven Changes to download the dependency package. This way, the Californium framework, Apache commons toolkit, and Alibaba Cloud fastjson package are imported.
    <dependency>
      <groupId>org.eclipse.californium</groupId>
      <artifactId>californium-core</artifactId>
      <version>2.0.0-M17</version>
    </dependency>
    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.5</version>
    </dependency>
    <dependency>
      <groupId>commons-codec</groupId>
      <artifactId>commons-codec</artifactId>
      <version>1.13</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 IotCoap-demo project, create a Java class such as IotCoapClientWithAes.java, and enter the code. Sample code:
    /*   
     * Copyright 2019 Alibaba. All rights reserved.
     */
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Set;
    import java.util.SortedMap;
    import java.util.TreeMap;
    
    import javax.crypto.Cipher;
    import javax.crypto.Mac;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    
    import org.apache.commons.codec.DecoderException;
    import org.apache.commons.codec.binary.Hex;
    import org.apache.commons.lang3.RandomUtils;
    import org.eclipse.californium.core.CoapClient;
    import org.eclipse.californium.core.CoapResponse;
    import org.eclipse.californium.core.Utils;
    import org.eclipse.californium.core.coap.CoAP;
    import org.eclipse.californium.core.coap.CoAP.Code;
    import org.eclipse.californium.core.coap.CoAP.Type;
    import org.eclipse.californium.core.coap.MediaTypeRegistry;
    import org.eclipse.californium.core.coap.Option;
    import org.eclipse.californium.core.coap.OptionNumberRegistry;
    import org.eclipse.californium.core.coap.OptionSet;
    import org.eclipse.californium.core.coap.Request;
    import org.eclipse.californium.elements.exception.ConnectorException;
    
    import com.alibaba.fastjson.JSONObject;
    
    /**
     * The following Eclipse Californium-based code shows how to connect a device to IoT Platform by using CoAP. 
     * For more information about the development process and parameters of autonomous access, see the "Connect devices by using symmetric encryption" section in the Connect devices to IoT Platform over CoAP topic. 
     */
    public class IotCoapClientWithAes {
    
        // ======================== You must specify the following parameters. ===========================
        // The region ID. China (Shanghai) is used in this example. 
        private static String regionId = "cn-shanghai";
        // The ProductKey of the product to which your device belongs. 
        private static String productKey = "ProductKey of your device";
        // The DeviceName of the device. 
        private static String deviceName = "DeviceName of your device";
        // The DeviceSecret of the device. 
        private static String deviceSecret = "DeviceSecret of your device";
        //======================= End ===========================
    
        // Set the HMAC_ALGORITHM parameter to hmacsha1 or hmacmd5. The value that you specify must be the same as that of the signmethod parameter. 
        private static final String HMAC_ALGORITHM = "hmacsha1";
    
        // The endpoint that is used to connect a device to an IoT Platform instance over CoAP. If you use symmetric encryption, the port number of the endpoint is 5682. 
        private static String serverURI = "coap://" + productKey + ".coap." + regionId + ".link.aliyuncs.com:5682";
    
        // The topic to which messages are sent. In the IoT Platform console, you can create a custom topic and grant devices the Publish permission on the topic. 
        private static String updateTopic = "/" + productKey + "/" + deviceName + "/user/update";
    
        // token option
        private static final int COAP2_OPTION_TOKEN = 2088;
        // seq option
        private static final int COAP2_OPTION_SEQ = 2089;
    
        // The SHA-256 encryption algorithm. 
        private static final String SHA_256 = "SHA-256";
    
        private static final int DIGITAL_16 = 16;
        private static final int DIGITAL_48 = 48;
    
        // The CoAP client. 
        private CoapClient coapClient = new CoapClient();
    
        // The validity period of a token is seven days. After a token expires, you must obtain a new token. 
        private String token = null;
        private String random = null;
        @SuppressWarnings("unused")
        private long seqOffset = 0;
    
        /**
         * Initialize the CoAP client. 
         * 
         * @param productKey: the ProductKey of the product to which your device belongs. 
         * @param deviceName: the DeviceName of your device. 
         * @param deviceSecret: the DeviceSecret of your device. 
         */
        public void connect(String productKey, String deviceName, String deviceSecret) {
            try {
                // The endpoint that is used for authentication. 
                String uri = serverURI + "/auth";
    
                // Only the POST method is supported. 
                Request request = new Request(Code.POST, Type.CON);
    
                // Specify options. 
                OptionSet optionSet = new OptionSet();
                optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON));
                optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON));
                request.setOptions(optionSet);
    
                // Specify the endpoint that you can use to authenticate the device. 
                request.setURI(uri);
    
                // Specify the required parameters for an authentication request. 
                request.setPayload(authBody(productKey, deviceName, deviceSecret));
    
                // Send the authentication request. 
                CoapResponse response = coapClient.advanced(request);
                System.out.println(Utils.prettyPrint(response));
                System.out.println();
    
                // Parse the response. 
                JSONObject json = JSONObject.parseObject(response.getResponseText());
                token = json.getString("token");
                random = json.getString("random");
                seqOffset = json.getLongValue("seqOffset");
            } catch (ConnectorException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Send a message. 
         * 
         @param topic The topic to which messages are sent. 
         * @param payload The content of the message. 
         */
        public void publish(String topic, byte[] payload) {
            try {
                // The endpoint of the topic. The syntax of a topic is /topic/${topic}. 
                String uri = serverURI + "/topic" + topic;
    
                // Encrypt the seq option by using the AES encryption algorithm. seq=RandomUtils.nextInt(). 
                String shaKey = encod(deviceSecret + "," + random);
                byte[] keys = Hex.decodeHex(shaKey.substring(DIGITAL_16, DIGITAL_48));
                byte[] seqBytes = encrypt(String.valueOf(RandomUtils.nextInt()).getBytes(StandardCharsets.UTF_8), keys);
    
                // Only the POST method is supported. 
                Request request = new Request(CoAP.Code.POST, CoAP.Type.CON);
    
                // Specify options. 
                OptionSet optionSet = new OptionSet();
                optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON));
                optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON));
                optionSet.addOption(new Option(COAP2_OPTION_TOKEN, token));
                optionSet.addOption(new Option(COAP2_OPTION_SEQ, seqBytes));
                request.setOptions(optionSet);
    
                // Specify the endpoint of the topic. 
                request.setURI(uri);
    
                // Specify the payload parameter. 
                request.setPayload(encrypt(payload, keys));
    
                // Send a message. 
                CoapResponse response = coapClient.advanced(request);
                System.out.println(Utils.prettyPrint(response));
    
                // Parse the result. 
                String result = null;
                if (response.getPayload() != null) {
                    result = new String(decrypt(response.getPayload(), keys));
                }
                System.out.println("payload: " + result);
                System.out.println();
            } catch (ConnectorException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (DecoderException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * Generate the parameters required for authentication. 
         * 
         * @param productKey: the ProductKey of the product to which your device belongs. 
         * @param deviceName: the DeviceName of the device. 
         * @param deviceSecret: the DeviceSecret of the device. 
         * @return: the authentication request. 
         */
        private String authBody(String productKey, String deviceName, String deviceSecret) {
    
            // Create an authentication request. 
            JSONObject body = new JSONObject();
            body.put("productKey", productKey);
            body.put("deviceName", deviceName);
            body.put("clientId", productKey + "." + deviceName);
            body.put("timestamp", String.valueOf(System.currentTimeMillis()));
            body.put("signmethod", HMAC_ALGORITHM);
            body.put("seq", DIGITAL_16);
            body.put("sign", sign(body, deviceSecret));
    
            System.out.println("----- auth body -----");
            System.out.println(body.toJSONString());
    
            return body.toJSONString();
        }
    
        /**
         * Generate a signature for a device. 
         * 
         * @param params: the parameters that are required to generate a signature. 
         * @param deviceSecret: the DeviceSecret of the device. 
         * @return: a hexadecimal signature string. 
         */
        private String sign(JSONObject params, String deviceSecret) {
    
            // Sort the request parameters in alphabetical order. 
            Set<String> keys = getSortedKeys(params);
    
            // Remove the sign, signmethod, version, and resources parameters. 
            keys.remove("sign");
            keys.remove("signmethod");
            keys.remove("version");
            keys.remove("resources");
    
            // 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(), deviceSecret);
            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 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: plaintext 
         * @param secret: encryption key 
         * @return: 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 Hex.encodeHexString(mac.doFinal(text));
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }
    
        /**
         * SHA-256
         * 
         * @param str: the message that you want to encrypt. 
         */
        private String encod(String str) {
            MessageDigest messageDigest;
            String encdeStr = "";
            try {
                messageDigest = MessageDigest.getInstance(SHA_256);
                byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
                encdeStr = Hex.encodeHexString(hash);
            } catch (NoSuchAlgorithmException e) {
                System.out.println(String.format("Exception@encod: str=%s;", str));
                e.printStackTrace();
                return null;
            }
            return encdeStr;
        }
    
        // Encrypt and decrypt data by using the AES algorithm. 
        private static final String IV = "543yhjy97ae7fyfg";
        private static final String TRANSFORM = "AES/CBC/PKCS5Padding";
        private static final String ALGORITHM = "AES";
    
        /**
         * key length = 16 bits
         */
        private byte[] encrypt(byte[] content, byte[] key) {
            return encrypt(content, key, IV);
        }
    
        /**
         * key length = 16 bits
         */
        private byte[] decrypt(byte[] content, byte[] key) {
            return decrypt(content, key, IV);
        }
    
        /**
         * aes 128 cbc key length = 16 bits
         */
        private byte[] encrypt(byte[] content, byte[] key, String ivContent) {
            try {
                SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
                Cipher cipher = Cipher.getInstance(TRANSFORM);
                IvParameterSpec iv = new IvParameterSpec(ivContent.getBytes(StandardCharsets.UTF_8));
                cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
                return cipher.doFinal(content);
            } catch (Exception ex) {
                System.out.println(
                        String.format("AES encrypt error, %s, %s, %s", content, Hex.encodeHex(key), ex.getMessage()));
                return null;
            }
        }
    
        /**
         * aes 128 cbc key length = 16 bits
         */
        private byte[] decrypt(byte[] content, byte[] key, String ivContent) {
            try {
                SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
                Cipher cipher = Cipher.getInstance(TRANSFORM);
                IvParameterSpec iv = new IvParameterSpec(ivContent.getBytes(StandardCharsets.UTF_8));
                cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
                return cipher.doFinal(content);
            } catch (Exception ex) {
                System.out.println(String.format("AES decrypt error, %s, %s, %s", Hex.encodeHex(content),
                        Hex.encodeHex(key), ex.getMessage()));
                return null;
            }
        }
    
        public static void main(String[] args) throws InterruptedException {
            IotCoapClientWithAes client = new IotCoapClientWithAes();
            client.connect(productKey, deviceName, deviceSecret);
            client.publish(updateTopic, "hello coap".getBytes(StandardCharsets.UTF_8));
            client.publish(updateTopic, new byte[] { 0x01, 0x02, 0x03, 0x05 });
        }
    }
  4. Specify the following parameters.
    Parameter Description
    regionId The ID of the region where IoT Platform resides.

    You can view the region in the upper-left corner of the IoT Platform console.

    For more information about region IDs, see Regions.

    productKey The device certificate information that you can obtain after you add the device to the IoT Platform console.

    You can view the information on the Device Details page in the IoT Platform console.

    deviceName
    deviceSecret
    serverURI The endpoint that is used to connect the device to IoT Platform over CoAP. For more information, see View the endpoint of an instance.
  5. Run the IotCoapClientWithAes.java program.
    The following sample code shows the execution result. The device can communicate with IoT Platform after the device is authenticated.
    sign content=clientIda1****RK0.devicedeviceNamedeviceproductKeya1OX****K0seq16timestamp1658909565141
    sign result=7f3b76dc21e7******fec424838d1858
    ----- auth body -----
    {"clientId":"a1OXp8sXRK0.device","signmethod":"hmacsha1","sign":"7f3b76dc21e7******fec424838d1858","productKey":"a1OX****K0","deviceName":"device","seq":16,"timestamp":"1658909565141"}
    ==[ CoAP Response ]============================================
    MID    : 37682
    Token  : 6E1F******6B91
    Type   : ACK
    Status : 2.05 - CONTENT
    Options: {}
    RTT    : 116 ms
    Payload: 85 Bytes
    ---------------------------------------------------------------
    {"random":"a25******6d44","seqOffset":1,"token":"mnx4OF0b******R000000.5ac7"}
    ===============================================================
    
    ==[ CoAP Response ]============================================
    MID    : 37683
    Token  : AC60******106E7
    Type   : ACK
    Status : 2.05 - CONTENT
    Options: {"Unknown (2090)":0x158a******6aa00}
    RTT    : 118 ms
    Payload: 0 Bytes
    ===============================================================
    payload: null
    
    ==[ CoAP Response ]============================================
    MID    : 37684
    Token  : AA9******EFCC
    Type   : ACK
    Status : 2.05 - CONTENT
    Options: {"Unknown (2090)":0x158a******f600}
    RTT    : 103 ms
    Payload: 0 Bytes
    ===============================================================
    payload: null