SDK สำหรับ TESAIoT Dev Kit
คู่มืออ้างอิง API และ Tutorial (MTB & µPython)
Loading...
Searching...
No Matches
C5 — TESAIoT cloud: HTTPS REST, Device API Key และ mTLS

เป้าหมายของหัวข้อนี้

บอร์ดส่งข้อมูลขึ้น TESAIoT ได้สองทาง บทที่ C3 เล่าทาง MQTT บทนี้เล่าอีกทาง

4 เรื่องที่ต้องรู้

  1. HTTPS เป็นเส้นทาง คำขอเดียวจบ ไม่มี session ไม่มี task ไม่มีคิว ต่างจาก MQTT ทุกประการ
  2. กุญแจมี สองชนิดและอยู่คนละช่องของ config ใส่ผิดช่องแล้วได้ 401 ที่อ่านเหมือนกันแต่คนละสาเหตุ
  3. payload ต้องมีฟิลด์ data เป็น object มิฉะนั้นได้ 400 VALIDATION_ERROR
  4. trust anchor ถูก pin ไว้ในเฟิร์มแวร์ เมื่อผู้ให้บริการใบรับรองของเซิร์ฟเวอร์เปลี่ยน บอร์ดจะตัดสายทิ้งก่อนคุย และนี่เกิดขึ้นมาแล้วจริง

เลือกทางไหน

MQTT (C3) HTTPS (บทนี้)
รูปแบบ ต่อค้างไว้ แล้ว publish ได้เรื่อย ๆ คำขอเดียว จบแล้วปิด
ยืนยันตัวตน ใบรับรองในชิป (mTLS) Device API Key
รับคำสั่งจากคลาวด์ได้ ได้ ผ่าน subscribe ไม่ได้
ต้นทุนหน่วยความจำ ถือ session TLS ไว้ตลอด คืนทั้งหมดเมื่อจบ
เหมาะกับ ส่งถี่ ต้องรับคำสั่ง ส่งนาน ๆ ครั้ง หรืออุปกรณ์ที่หลับเป็นหลัก

ห้ามเปิดพร้อมกัน วัดบนบอร์ดจริงเมื่อ 2026-08-31 ขณะ MQTT ต่ออยู่ heap เหลือ 13,128 ไบต์ และ malloc(32768) ล้มเหลว การต่อ TLS ที่สองจึงไปไม่ถึง handshake ถ้าไม่มี MQTT heap เหลือ 71,712 ไบต์ และผ่านสบาย

กุญแจสองชนิด อยู่คนละช่อง

นี่คือจุดที่เสียเวลามากที่สุด และมันไม่ชัดเจนจากชื่อ

ช่องใน config หัวข้อ HTTP ที่ส่ง รูปแบบกุญแจ ใช้กับ
apisix_api_key apikey: tesa_ak_… APISIX gateway
api_key ค่าใน api_key_header ปริยาย X-API-KEY tesa_dak_… backend ของแพลตฟอร์ม

ipc_tesaiot_defs.h:40 เขียนกำกับไว้ว่า apisix_api_key คือ "RESTful API key (tesa_ak_...)" ซึ่งอ่านแล้วชวนให้ใส่ผิด

บน api.tesaiot.dev ให้ใช้ api_key เท่านั้น วัดจริงด้วย curl สี่แบบ ส่งเฉพาะหัวข้อ apikey ได้ผลลัพธ์ เหมือนกับไม่ส่งอะไรเลย คือ AUTH_MISSING ทั้งคู่ แปลว่าโฮสต์นี้ไม่มี APISIX คั่นอยู่

import tesaiot
tesaiot.config_set("api_host", "api.tesaiot.dev")
tesaiot.config_set("api_key", "tesa_dak_<device>_<hex>")
tesaiot.config_set("api_key_header", "X-API-KEY")

Device API Key หาได้จากหน้า Device Credentials ของแพลตฟอร์ม และแสดงครั้งเดียว

รูปแบบข้อมูล

{"device_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"data": {"temperature": 31.4, "humidity": 60}}

ต้องมี data เป็น object ค่าที่แบนอยู่ชั้นบนจะได้

400 {"code":"VALIDATION_ERROR","error":"data field is required and must be an object"}

เมื่อสำเร็จ

200 {"success":true,"message":"Telemetry data stored successfully",
"metrics_stored":["temperature"],"telemetry_id":"…"}

trust anchor และเหตุที่มันพังมาแล้ว

tesaiot_https.c ตั้ง socket option สามตัวเท่านั้น — CA ที่เชื่อ, SNI, และโหมดตรวจ — โดย CA มาจาก TESAIOT_HTTPS_ROOT_CA ใน tesaiot_https_root_ca.h

เดิม pin ไว้ที่ Let's Encrypt E7 ต่อมาปลายทางย้ายไปใบที่ออกโดย YE2 ผลคือทุกคำขอตายที่

[TLS-DIAG] err=-0x2700 vf=0x00000008 BADCERT_NOT_TRUSTED
[HTTPS] Connect failed: 0x082A000E

บอร์ดตัดสายทิ้งก่อนแลกเปลี่ยน HTTP แม้แต่ไบต์เดียว ซึ่งอ่านเหมือนเครือข่ายมีปัญหา แต่เป็นการทำงานที่ถูกต้องของ TLS

ปัจจุบัน pin ที่ ISRG Root YE ซึ่งเป็นผู้ออก YE2 อีกทีหนึ่ง

ทำไม pin ที่ root ไม่ใช่ intermediate เพราะ intermediate หมุนตามรอบ การ pin ที่มันคือสาเหตุของเหตุขัดข้องข้างต้นเอง Root YE อยู่ถึง 2032-09-02

ทำไมไม่ pin ISRG Root X1 เพราะเป็น RSA-4096 และการตรวจใบขนาดนั้นทำให้ heap บนชิปนี้ไม่พอ ส่วนสายโซ่ที่เซิร์ฟเวอร์ส่งมาเป็น ECDSA P-384 ทั้งสี่ใบ จึงไม่ต้องเดินเส้นทางนั้นเลย

วิธีตรวจเมื่อสงสัยว่า CA เปลี่ยน

openssl s_client -connect api.tesaiot.dev:443 -servername api.tesaiot.dev \
-showcerts </dev/null 2>/dev/null | openssl x509 -noout -issuer

ถ้า issuer ไม่ตรงกับที่ pin ไว้ ต้องเปลี่ยนเฮดเดอร์แล้ว build ใหม่

mTLS บนเส้นทาง HTTPS

เมื่อ tls_mode เป็น TESAIOT_MODE_MTLS เส้นทาง HTTPS จะเรียก mqtt_mtls_setup_optiga() เพื่อยกตัวตนจาก OPTIGA ขึ้นมาก่อนต่อ ฟังก์ชันนี้ชื่อชวนเข้าใจผิด — ไม่มีอะไรในนั้นเกี่ยวกับ MQTT มันอ่านใบรับรองจากชิปแล้วส่งให้ตัวแปรกลางสองตัวใน cy_tls.c ซึ่ง cy_tls_connect() อ่านทุกครั้งที่ต่อ

ประกาศไว้แบบ weak และตรวจ NULL ก่อนเรียก บิลด์ที่ไม่ได้คอมไพล์ส่วนนี้จึงยังลิงก์ได้และถอยไปใช้ API key

เมื่อทำงานสำเร็จจะเห็น

[mTLS] device pair verified — using TESAIoT identity
[PSA-SE] Setting signing key OID to 0xE0F1
[mTLS] Certificate read: 730 bytes PEM
[TLS-DIAG] optiga_key=2147483616 cert_ready=1
[TLS-DIAG] mbedtls_pk_setup_opaque = 0
[TLS-DIAG] mbedtls_ssl_conf_own_cert = 0

แต่ยังใช้ยืนยันตัวตนไม่ได้ ณ วันที่เขียน เพราะเซิร์ฟเวอร์ไม่ได้ขอใบรับรองจากลูกค้า

openssl s_client -connect api.tesaiot.dev:443 -servername api.tesaiot.dev </dev/null 2>&1 \
| grep -i 'client certificate'
# No client certificate CA names sent

เมื่อไม่มีการร้องขอ TLS จะไม่ส่งใบไปเลย ต่อให้บอร์ดเตรียมพร้อมสมบูรณ์ ข้อความ 401 ของ API ที่เขียนว่า "or use mTLS certificate" จึงเป็นข้อความมาตรฐาน ไม่ได้แปลว่าโฮสต์นี้เปิดโหมดนั้นไว้ ต้องเปิดฝั่งเซิร์ฟเวอร์ก่อน

กับดัก

  • 401 สองแบบ อ่านให้ออก AUTH_MISSING แปลว่าเซิร์ฟเวอร์ไม่เห็นหัวข้อยืนยันตัวตนเลย ส่วน API_KEY_INVALID แปลว่ามันเห็นแล้วแต่ปฏิเสธค่า สองอย่างนี้ชี้ไปคนละที่
  • อย่าเปิด MQTT ค้างไว้แล้วยิง HTTPS heap ไม่พอ
  • api.tesaiot.com กับ api.tesaiot.dev เป็นคนละเครื่องและคนละ CA .com ใช้ใบของ Amazon .dev ใช้ Let's Encrypt เฟิร์มแวร์ pin ของ .dev ไว้
  • ทดสอบด้วย curl จากเครื่องก่อนเสมอ ถ้า curl ก็ 401 ปัญหาไม่ได้อยู่ที่บอร์ด