Topik ini menjelaskan cara menghubungkan perangkat ke IoT Platform menggunakan Constrained Application Protocol (CoAP). Dalam contoh ini, kode Java digunakan.
Informasi latar belakang
CoAP cocok untuk perangkat hemat daya dan dengan sumber daya terbatas, seperti perangkat NB-IoT. Untuk informasi tentang cara menghubungkan perangkat ke IoT Platform menggunakan CoAP, lihat Menghubungkan Perangkat ke IoT Platform melalui CoAP.
Saat menghubungkan perangkat ke IoT Platform menggunakan CoAP, Anda harus menentukan parameter yang diperlukan, menghasilkan tanda tangan untuk perangkat, dan melakukan operasi lainnya. Kode contoh berbasis Eclipse Californium digunakan untuk menggambarkan proses konfigurasi. Untuk memastikan keamanan data, enkripsi simetris digunakan.
Menyiapkan lingkungan pengembangan
Lingkungan pengembangan Java berikut siap digunakan.
Sistem Operasi: Windows 10
Java Development Kit (JDK): JDK 8
Integrated Development Environment (IDE): IntelliJ IDEA Community Edition
Prosedur
- Buka IntelliJ IDEA dan buat proyek Maven. Contoh: IotCoap-demo.
- Tambahkan dependensi berikut ke file pom.xml dalam proyek, lalu klik Load Maven Changes untuk mengunduh paket dependensi. Dengan cara ini, kerangka kerja Californium, toolkit Apache Commons, dan paket fastjson Alibaba Cloud diimpor.
<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> - Di direktori /src/main/java dari proyek IotCoap-demo, buat kelas Java seperti IotCoapClientWithAes.java, dan masukkan kode. Contoh kode:
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; /** * Kode berbasis Eclipse Californium berikut menunjukkan cara menghubungkan perangkat ke IoT Platform menggunakan CoAP. * Untuk informasi lebih lanjut tentang proses pengembangan dan parameter akses otonom, lihat bagian "Menghubungkan perangkat menggunakan enkripsi simetris" dalam topik Menghubungkan perangkat ke IoT Platform melalui CoAP. */ public class IotCoapClientWithAes { // ======================== Anda harus menentukan parameter berikut. =========================== // ID wilayah. Tiongkok (Shanghai) digunakan dalam contoh ini. private static String regionId = "cn-shanghai"; // ProductKey produk tempat perangkat Anda termasuk. private static String productKey = "ProductKey perangkat Anda"; // DeviceName perangkat. private static String deviceName = "DeviceName perangkat Anda"; // DeviceSecret perangkat. private static String deviceSecret = "DeviceSecret perangkat Anda"; //======================= Akhir =========================== // Tetapkan parameter HMAC_ALGORITHM ke hmacsha1 atau hmacmd5. Nilai yang Anda tentukan harus sama dengan nilai parameter signmethod. private static final String HMAC_ALGORITHM = "hmacsha1"; // Titik akhir yang digunakan untuk menghubungkan perangkat ke instance IoT Platform melalui CoAP. Jika Anda menggunakan enkripsi simetris, nomor port titik akhir adalah 5682. private static String serverURI = "coap://" + productKey + ".coap." + regionId + ".link.aliyuncs.com:5682"; // Topik tempat pesan dikirim. Di konsol IoT Platform, Anda dapat membuat topik kustom dan memberikan izin Publish pada topik kepada perangkat. 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; // Algoritma enkripsi SHA-256. private static final String SHA_256 = "SHA-256"; private static final int DIGITAL_16 = 16; private static final int DIGITAL_48 = 48; // Klien CoAP. private CoapClient coapClient = new CoapClient(); // Masa berlaku token adalah tujuh hari. Setelah token kedaluwarsa, Anda harus mendapatkan token baru. private String token = null; private String random = null; @SuppressWarnings("unused") private long seqOffset = 0; /** * Inisialisasi klien CoAP. * * @param productKey: ProductKey produk tempat perangkat Anda termasuk. * @param deviceName: DeviceName perangkat Anda. * @param deviceSecret: DeviceSecret perangkat Anda. */ public void connect(String productKey, String deviceName, String deviceSecret) { try { // Titik akhir yang digunakan untuk autentikasi. String uri = serverURI + "/auth"; // Hanya metode POST yang didukung. Request request = new Request(Code.POST, Type.CON); // Tentukan opsi. 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); // Tentukan titik akhir yang dapat Anda gunakan untuk mengautentikasi perangkat. request.setURI(uri); // Tentukan parameter yang diperlukan untuk permintaan autentikasi. request.setPayload(authBody(productKey, deviceName, deviceSecret)); // Kirim permintaan autentikasi. CoapResponse response = coapClient.advanced(request); System.out.println(Utils.prettyPrint(response)); System.out.println(); // Analisis respons. 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(); } } /** * Kirim pesan. * @param topic Topik tempat pesan dikirim. * @param payload Isi pesan. */ public void publish(String topic, byte[] payload) { try { // Titik akhir topik. Sintaks topik adalah /topic/${topic}. String uri = serverURI + "/topic" + topic; // Enkripsi opsi seq menggunakan algoritma enkripsi AES. 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); // Hanya metode POST yang didukung. Request request = new Request(CoAP.Code.POST, CoAP.Type.CON); // Tentukan opsi. 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); // Tentukan titik akhir topik. request.setURI(uri); // Tentukan parameter payload. request.setPayload(encrypt(payload, keys)); // Kirim pesan. CoapResponse response = coapClient.advanced(request); System.out.println(Utils.prettyPrint(response)); // Analisis hasil. 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(); } } /** * Hasilkan parameter yang diperlukan untuk autentikasi. * * @param productKey: ProductKey produk tempat perangkat Anda termasuk. * @param deviceName: DeviceName perangkat. * @param deviceSecret: DeviceSecret perangkat. * @return: permintaan autentikasi. */ private String authBody(String productKey, String deviceName, String deviceSecret) { // Buat permintaan autentikasi. 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(); } /** * Hasilkan tanda tangan untuk perangkat. * * @param params: parameter yang diperlukan untuk menghasilkan tanda tangan. * @param deviceSecret: DeviceSecret perangkat. * @return: string tanda tangan heksadesimal. */ private String sign(JSONObject params, String deviceSecret) { // Urutkan parameter permintaan secara alfabetis. Set<String> keys = getSortedKeys(params); // Hapus parameter sign, signmethod, version, dan resources. keys.remove("sign"); keys.remove("signmethod"); keys.remove("version"); keys.remove("resources"); // Dapatkan plaintext tanda tangan. StringBuffer content = new StringBuffer(); for (String key : keys) { content.append(key); content.append(params.getString(key)); } // Hasilkan tanda tangan. String sign = encrypt(content.toString(), deviceSecret); System.out.println("sign content=" + content); System.out.println("sign result=" + sign); return sign; } /** * Urutkan kunci objek JSON secara alfabetis. * * @param json: objek JSON yang ingin Anda urutkan kuncinya. * @return: set kunci yang diurutkan secara alfabetis. */ 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(); } /** * Tentukan algoritma enkripsi menggunakan parameter HMAC_ALGORITHM. * * @param content: plaintext * @param secret: kunci enkripsi * @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: pesan yang ingin Anda enkripsi. */ 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; } // Enkripsi dan dekripsi data menggunakan algoritma AES. private static final String IV = "543yhjy97ae7fyfg"; private static final String TRANSFORM = "AES/CBC/PKCS5Padding"; private static final String ALGORITHM = "AES"; /** * panjang kunci = 16 bit */ private byte[] encrypt(byte[] content, byte[] key) { return encrypt(content, key, IV); } /** * panjang kunci = 16 bit */ private byte[] decrypt(byte[] content, byte[] key) { return decrypt(content, key, IV); } /** * aes 128 cbc panjang kunci = 16 bit */ 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 panjang kunci = 16 bit */ 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 }); } } - Tentukan parameter berikut.
Parameter Deskripsi regionId ID wilayah tempat IoT Platform berada. Anda dapat melihat wilayah tersebut di pojok kiri atas konsol IoT Platform.
Untuk informasi lebih lanjut tentang ID wilayah, lihat Wilayah.
productKey Informasi sertifikat perangkat yang dapat Anda peroleh setelah menambahkan perangkat ke konsol IoT Platform. Anda dapat melihat informasi tersebut di halaman Device Details di konsol IoT Platform.
deviceName deviceSecret serverURI Titik akhir yang digunakan untuk menghubungkan perangkat ke IoT Platform melalui CoAP. Untuk informasi lebih lanjut, lihat Kelola titik akhir sebuah instans. - Jalankan program IotCoapClientWithAes.java.Berikut ini adalah contoh kode yang menunjukkan hasil eksekusi. Perangkat dapat berkomunikasi dengan IoT Platform setelah perangkat diautentikasi.
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