本文以一型一密預(yù)注冊(cè)的Java代碼為例,介紹基于HTTPS通信協(xié)議的設(shè)備,如何進(jìn)行動(dòng)態(tài)注冊(cè)并獲取接入物聯(lián)網(wǎng)平臺(tái)認(rèn)證需要的DeviceSecret。

前提條件

已完成一型一密文檔中的以下步驟:

  1. 創(chuàng)建產(chǎn)品。
  2. 開(kāi)啟動(dòng)態(tài)注冊(cè)。
  3. 添加設(shè)備。
  4. 產(chǎn)線燒錄。

背景信息

物聯(lián)網(wǎng)平臺(tái)支持多種設(shè)備安全認(rèn)證方式,具體認(rèn)證方式,請(qǐng)參見(jiàn)設(shè)備安全認(rèn)證

物聯(lián)網(wǎng)平臺(tái)支持基于HTTPS通道實(shí)現(xiàn)一型一密的預(yù)注冊(cè)認(rèn)證。相關(guān)Topic和參數(shù)說(shuō)明,請(qǐng)參見(jiàn)直連設(shè)備的HTTPS動(dòng)態(tài)注冊(cè)

準(zhǔn)備開(kāi)發(fā)環(huán)境

本示例使用的開(kāi)發(fā)環(huán)境如下:

操作步驟

  1. 打開(kāi)IntelliJ IDEA,創(chuàng)建一個(gè)Maven工程。例如HTTPS動(dòng)態(tài)注冊(cè)
  2. 在工程中的pom.xml文件中,添加Maven依賴,然后單擊Load Maven Changes圖標(biāo),完成依賴包下載。
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>fastjson</artifactId>
      <version>1.2.83</version>
    </dependency>
  3. 在工程HTTPS動(dòng)態(tài)注冊(cè)的路徑/src/main/java下,創(chuàng)建Java類。例如DynamicRegisterByHttps.java,輸入以下代碼。
    說(shuō)明
    • 設(shè)備未激活時(shí),可進(jìn)行多次動(dòng)態(tài)注冊(cè),設(shè)備的DeviceSecret以最后一次為準(zhǔn)。請(qǐng)確保固化到設(shè)備的DeviceSecret為最新。
    • 設(shè)備已激活時(shí),您需調(diào)用ResetThing接口重置云端設(shè)備動(dòng)態(tài)注冊(cè)狀態(tài)為未注冊(cè),才能再次動(dòng)態(tài)注冊(cè)該設(shè)備。
    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;
    
    /**
     * 設(shè)備動(dòng)態(tài)注冊(cè)。
     */
    public class DynamicRegisterByHttps {
    
        // 地域ID,填寫您的產(chǎn)品所在地域ID。
        private static String regionId = "cn-shanghai";
    
        // 定義加密方式。可選MAC算法:HmacMD5、HmacSHA1、HmacSHA256,需和signmethod一致。
        private static final String HMAC_ALGORITHM = "hmacsha1";
    
        /**
         * 動(dòng)態(tài)注冊(cè)。
         * 
         * @param productKey 產(chǎn)品key
         * @param productSecret 產(chǎn)品密鑰
         * @param deviceName 設(shè)備名稱
         * @throws Exception
         */
        public void register(String productKey, String productSecret, String deviceName) throws Exception {
    
            // 請(qǐng)求地址。
            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對(duì)象對(duì)應(yīng)的輸出流。
            PrintWriter out = new PrintWriter(conn.getOutputStream());
            // 發(fā)送請(qǐng)求參數(shù)。
            out.print(registerdBody(productKey, productSecret, deviceName));
            // flush輸出流的緩沖。
            out.flush();
    
            // 獲取URLConnection對(duì)象對(duì)應(yīng)的輸入流。
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            // 讀取URL的響應(yīng)。
            String result = "";
            String line = "";
            while ((line = in.readLine()) != null) {
                result += line;
            }
            System.out.println("----- register result -----");
            System.out.println(result);
    
            // 關(guān)閉輸入輸出流。
            in.close();
            out.close();
            conn.disconnect();
        }
    
        /**
         * 生成動(dòng)態(tài)注冊(cè)請(qǐng)求內(nèi)容。
         * 
         * @param productKey 產(chǎn)品ProductKey
         * @param productSecret 產(chǎn)品密鑰
         * @param deviceName 設(shè)備名稱
         * @return 動(dòng)態(tài)注冊(cè)payload
         */
        private String registerdBody(String productKey, String productSecret, String deviceName) {
    
            // 獲取隨機(jī)值。
            Random r = new Random();
            int random = r.nextInt(1000000);
    
            // 動(dòng)態(tài)注冊(cè)參數(shù)。
            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();
        }
    
        /**
         * 動(dòng)態(tài)注冊(cè)簽名。
         * 
         * @param params 簽名參數(shù)
         * @param productSecret 產(chǎn)品密鑰
         * @return 簽名十六進(jìn)制字符串
         */
        private String sign(JSONObject params, String productSecret) {
    
            // 請(qǐng)求參數(shù)按字典順序排序。
            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));
            }
    
            // 計(jì)算簽名。
            String sign = encrypt(content.toString(), productSecret);
            System.out.println("sign content=" + content);
            System.out.println("sign result=" + sign);
    
            return sign;
        }
    
        /**
         * 獲取JSON對(duì)象排序后的key集合。
         *
         * @param json 需要排序的JSON對(duì)象
         * @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;
            }
        }
    
        /**
         * 二進(jìn)制轉(zhuǎn)十六進(jìn)制字符串。
         * 
         * @param b 二進(jìn)制數(shù)組
         * @return 十六進(jìn)制字符串
         */
        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";
    
            // 進(jìn)行動(dòng)態(tài)注冊(cè)。
            DynamicRegisterByHttps client = new DynamicRegisterByHttps();
            client.register(productKey, productSecret, deviceName);
    
            // 動(dòng)態(tài)注冊(cè)成功后,需要固化deviceSecret。
        }
    }
  4. 在以上代碼中配置實(shí)際設(shè)備相關(guān)參數(shù)。
    參數(shù) 示例 說(shuō)明
    regionId cn-shanghai 您的物聯(lián)網(wǎng)平臺(tái)服務(wù)所在地域ID。地域代碼表達(dá)方法,請(qǐng)參見(jiàn)地域列表
    productKey a1IoK****** 已燒錄至設(shè)備的產(chǎn)品ProductKey,可登錄物聯(lián)網(wǎng)平臺(tái)控制臺(tái),在產(chǎn)品詳情頁(yè)查看。
    productSecret 6vEu5Qlj5S****** 已燒錄至設(shè)備的產(chǎn)品ProductSecret,可登錄物聯(lián)網(wǎng)平臺(tái)控制臺(tái),在產(chǎn)品詳情頁(yè)查看。
    deviceName OvenDevice01 您設(shè)備的名稱。

    因設(shè)備激活時(shí)會(huì)校驗(yàn)DeviceName,建議您采用可以直接從設(shè)備中讀取到的ID,如設(shè)備的MAC地址、IMEI或SN碼等,作為DeviceName使用。

  5. 運(yùn)行程序文件DynamicRegisterByHttps.java,使設(shè)備攜帶DeviceName和所屬產(chǎn)品的ProductKey、ProductSecret向云端發(fā)起認(rèn)證請(qǐng)求。執(zhí)行結(jié)果如圖所示,物聯(lián)網(wǎng)平臺(tái)校驗(yàn)通過(guò)后,設(shè)備接收到云端下發(fā)的DeviceSecret(6b14088fa377e8f852d82f7f********)。代碼運(yùn)行結(jié)果-https.gif

后續(xù)步驟

設(shè)備獲得連接云端所需的設(shè)備證書(ProductKey、DeviceName和DeviceSecret)后,您再使用MQTT客戶端,將設(shè)備接入物聯(lián)網(wǎng)平臺(tái),進(jìn)行數(shù)據(jù)通信。

具體操作,請(qǐng)參見(jiàn)Paho-MQTT Java接入示例