本文以一型一密預(yù)注冊(cè)的Java代碼為例,介紹基于HTTPS通信協(xié)議的設(shè)備,如何進(jìn)行動(dòng)態(tài)注冊(cè)并獲取接入物聯(lián)網(wǎng)平臺(tái)認(rèn)證需要的DeviceSecret。
前提條件
已完成一型一密文檔中的以下步驟:
- 創(chuàng)建產(chǎn)品。
- 開(kāi)啟動(dòng)態(tài)注冊(cè)。
- 添加設(shè)備。
- 產(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)境如下:
- 操作系統(tǒng):Windows 10
- JDK版本:JDK8
- 集成開(kāi)發(fā)環(huán)境:IntelliJ IDEA社區(qū)版
操作步驟
- 打開(kāi)IntelliJ IDEA,創(chuàng)建一個(gè)Maven工程。例如HTTPS動(dòng)態(tài)注冊(cè)。
- 在工程中的pom.xml文件中,添加Maven依賴,然后單擊Load Maven Changes圖標(biāo),完成依賴包下載。
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.83</version> </dependency>
- 在工程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。 } }
- 在以上代碼中配置實(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使用。
- 運(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********
)。
后續(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接入示例。