本文以一型一密预注册的Java代码为例,介绍基于HTTPS通信协议的设备,如何进行动态注册并获取接入物联网平台认证需要的DeviceSecret。

前提条件

已完成一型一密文档中的以下步骤:

  1. 创建产品。
  2. 开启动态注册。
  3. 添加设备。
  4. 产线烧录。

背景信息

物联网平台支持多种设备安全认证方式,具体认证方式,请参见设备安全认证

物联网平台支持基于HTTPS通道实现一型一密的预注册认证。相关Topic和参数说明,请参见直连设备的HTTPS动态注册

准备开发环境

本示例使用的开发环境如下:

操作步骤

  1. 打开IntelliJ IDEA,创建一个Maven工程。例如HTTPS动态注册
  2. 在工程中的pom.xml文件中,添加Maven依赖,然后单击Load Maven Changes图标,完成依赖包下载。
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.83</version>
    </dependency>
  3. 在工程HTTPS动态注册的路径/src/main/java下,创建Java类。例如DynamicRegisterByHttps.java,输入以下代码。
    说明
    • 设备未激活时,可进行多次动态注册,设备的DeviceSecret以最后一次为准。请确保固化到设备的DeviceSecret为最新。
    • 设备已激活时,您需调用ResetThing接口重置云端设备动态注册状态为未注册,才能再次动态注册该设备。
    import java.io.BufferedReader;
    import java.io.InputStreamReader;
    import java.io.PrintWriter;
    import java.net.URL;
    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 javax.net.ssl.HttpsURLConnection;
    
    import com.alibaba.fastjson.JSONObject;
    
    /**
     * 设备动态注册。
     */
    public class DynamicRegisterByHttps {
    
        // 地域ID,填写您的产品所在地域ID。
        private static String regionId = "cn-shanghai";
    
        // 定义加密方式。可选MAC算法:HmacMD5、HmacSHA1、HmacSHA256,需和signmethod一致。
        private static final String HMAC_ALGORITHM = "hmacsha1";
    
        /**
         * 动态注册。
         * 
         * @param productKey 产品key
         * @param productSecret 产品密钥
         * @param deviceName 设备名称
         * @throws Exception
         */
        public void register(String productKey, String productSecret, String deviceName) throws Exception {
    
            // 请求地址。
            URL url = new URL("https://iot-auth." + regionId + ".aliyuncs.com/auth/register/device");
    
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setRequestMethod("POST");
            conn.setRequestProperty("Content-type", "application/x-www-form-urlencoded");
            conn.setDoOutput(true);
            conn.setDoInput(true);
    
            // 获取URLConnection对象对应的输出流。
            PrintWriter out = new PrintWriter(conn.getOutputStream());
            // 发送请求参数。
            out.print(registerdBody(productKey, productSecret, deviceName));
            // flush输出流的缓冲。
            out.flush();
    
            // 获取URLConnection对象对应的输入流。
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            // 读取URL的响应。
            String result = "";
            String line = "";
            while ((line = in.readLine()) != null) {
                result += line;
            }
            System.out.println("----- register result -----");
            System.out.println(result);
    
            // 关闭输入输出流。
            in.close();
            out.close();
            conn.disconnect();
        }
    
        /**
         * 生成动态注册请求内容。
         * 
         * @param productKey 产品ProductKey
         * @param productSecret 产品密钥
         * @param deviceName 设备名称
         * @return 动态注册payload
         */
        private String registerdBody(String productKey, String productSecret, String deviceName) {
    
            // 获取随机值。
            Random r = new Random();
            int random = r.nextInt(1000000);
    
            // 动态注册参数。
            JSONObject params = new JSONObject();
            params.put("productKey", productKey);
            params.put("deviceName", deviceName);
            params.put("random", random);
            params.put("signMethod", HMAC_ALGORITHM);
            params.put("sign", sign(params, productSecret));
    
            // 拼接payload。
            StringBuffer payload = new StringBuffer();
            for (String key : params.keySet()) {
                payload.append(key);
                payload.append("=");
                payload.append(params.getString(key));
                payload.append("&");
            }
            payload.deleteCharAt(payload.length() - 1);
    
            System.out.println("----- register payload -----");
            System.out.println(payload);
    
            return payload.toString();
        }
    
        /**
         * 动态注册签名。
         * 
         * @param params 签名参数
         * @param productSecret 产品密钥
         * @return 签名十六进制字符串
         */
        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对象排序后的key集合。
         *
         * @param json 需要排序的JSON对象
         * @return 排序后的key集合
         */
        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;
            }
        }
    
        /**
         * 二进制转十六进制字符串。
         * 
         * @param b 二进制数组
         * @return 十六进制字符串
         */
        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";
    
            // 进行动态注册。
            DynamicRegisterByHttps client = new DynamicRegisterByHttps();
            client.register(productKey, productSecret, deviceName);
    
            // 动态注册成功后,需要固化deviceSecret。
        }
    }
  4. 在以上代码中配置实际设备相关参数。
    参数 示例 说明
    regionId cn-shanghai 您的物联网平台服务所在地域ID。地域代码表达方法,请参见地域列表
    productKey a1IoK****** 已烧录至设备的产品ProductKey,可登录物联网平台控制台,在产品详情页查看。
    productSecret 6vEu5Qlj5S****** 已烧录至设备的产品ProductSecret,可登录物联网平台控制台,在产品详情页查看。
    deviceName OvenDevice01 您设备的名称。

    因设备激活时会校验DeviceName,建议您采用可以直接从设备中读取到的ID,如设备的MAC地址、IMEI或SN码等,作为DeviceName使用。

  5. 运行程序文件DynamicRegisterByHttps.java,使设备携带DeviceName和所属产品的ProductKey、ProductSecret向云端发起认证请求。执行结果如图所示,物联网平台校验通过后,设备接收到云端下发的DeviceSecret(6b14088fa377e8f852d82f7f********)。代码运行结果-https.gif

后续步骤

设备获得连接云端所需的设备证书(ProductKey、DeviceName和DeviceSecret)后,您再使用MQTT客户端,将设备接入物联网平台,进行数据通信。

具体操作,请参见Paho-MQTT Java接入示例