概要
TLS(Transport Layer Security)は、ネットワーク通信に暗号化・認証・完全性保護を提供するプロトコルである。旧称は SSL(Secure Sockets Layer)であり、HTTPS、SMTPS、MQTTS など多くのセキュア通信プロトコルの基盤となっている。
DTLS(Datagram TLS)は TLS の UDP 版であり、TCP を前提とする TLS をパケット損失や順序入れ替えが起こりうる UDP 環境で動作するように拡張したものである。CoAP over DTLS(coaps://)など、リソース制約のある IoT デバイスや UDP ベースの通信に使用される。
TLS は「通信路の保護」を担い、PKI/証明書は「通信相手の身元保証」を担う。両者が組み合わさって初めて、安全で信頼できる通信が実現する。
歴史・背景
- SSL 1.0 〜 3.0(1994〜1996年): Netscape 社が開発。1995 年の SSL 2.0 から HTTPS が実用化された。SSL 3.0 には深刻な脆弱性(POODLE 等)があり、現在は使用禁止。
- TLS 1.0(1999年、RFC 2246): SSL 3.0 の後継として IETF が標準化。2021 年に RFC 8996 で廃止勧告。
- TLS 1.2(2008年、RFC 5246): SHA-256、GCM モード対応。長らく主流。2024 年以降は新規実装で非推奨扱い。
- DTLS 1.0(2006年、RFC 4347): TLS の UDP 版として登場。CoAP や SRTP で使用。
- TLS 1.3(2018年、RFC 8446): 設計を大幅に刷新。ハンドシェイクを高速化(1-RTT)、前方秘匿性を必須化。現在の推奨バージョン。
- DTLS 1.3(2022年、RFC 9147): TLS 1.3 の改善を DTLS に適用。IoT での採用が進む。
- RFC 7925(2016年): IoT 向け TLS/DTLS のプロファイルが定義。リソース制約環境でのベストプラクティスを規定。
技術仕様
TLS 1.3 vs TLS 1.2 の比較
| 項目 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| ハンドシェイク RTT | 2 RTT | 1 RTT(0-RTT も可能) |
| 前方秘匿性 | オプション | 必須(ECDHE/DHE のみ) |
| 廃止されたもの | — | RSA 鍵交換、CBC モード、SHA-1、MD5 |
| 暗号スイート | 多数(選定複雑) | 5 種類に限定(シンプル) |
| ハンドシェイクの暗号化 | なし | あり(Certificate も暗号化) |
| セッション再開 | Session ID/Ticket | PSK ベース |
TLS 1.3 の暗号スイート(5 種類のみ)
TLS_AES_128_GCM_SHA256 (最も一般的)
TLS_AES_256_GCM_SHA384 (高セキュリティ)
TLS_CHACHA20_POLY1305_SHA256 (ソフトウェア実装に最適)
TLS_AES_128_CCM_SHA256 (IoT/組み込み向け)
TLS_AES_128_CCM_8_SHA256 (超低リソース IoT 向け)
TLS vs DTLS の比較
| 項目 | TLS | DTLS |
|---|---|---|
| トランスポート | TCP | UDP |
| パケット損失 | TCP が再送を保証 | DTLS レイヤーで再送処理 |
| 順序保証 | TCP が保証 | DTLS がシーケンス番号管理 |
| ブロッキング | あり(Head-of-Line) | なし |
| 主な用途 | HTTPS、MQTT、AMQP | CoAP、SRTP、QUIC(前身) |
| RFC | RFC 8446(TLS 1.3) | RFC 9147(DTLS 1.3) |
動作原理
TLS 1.3 ハンドシェイク
クライアント サーバー
│ │
│─── ClientHello ──────────────→ │
│ supported_versions: [TLS1.3] │
│ cipher_suites: [...] │
│ key_share: ECDH公開鍵 │
│ │
│ ←── ServerHello ─────────────── │
│ key_share: ECDH公開鍵 │
│ ↑ここで ECDH 鍵交換完了 │
│ │
│ ←── EncryptedExtensions ──────── │ 以後は暗号化
│ ←── Certificate ─────────────── │ サーバー証明書
│ ←── CertificateVerify ────────── │ 証明書の署名検証
│ ←── Finished ─────────────────── │
│ │
│─── Certificate ──────────────→ │ クライアント認証(mTLS の場合)
│─── CertificateVerify ─────────→ │
│─── Finished ──────────────────→ │
│ │
│ ═══════════ 暗号化通信 ══════════ │
│ (1 RTT で完了!) │
組み込みでの Mbed TLS 実装例
#include "mbedtls/ssl.h"
#include "mbedtls/net_sockets.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/x509_crt.h"
/* TLS クライアントの初期化 */
typedef struct {
mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
mbedtls_net_context server_fd;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_x509_crt cacert; /* サーバー CA 証明書 */
mbedtls_x509_crt clicert; /* クライアント証明書 */
mbedtls_pk_context pkey; /* クライアント秘密鍵 */
} tls_client_t;
int tls_client_init(tls_client_t *client,
const char *ca_cert_pem,
const char *cli_cert_pem,
const char *cli_key_pem) {
/* 各コンポーネントの初期化 */
mbedtls_ssl_init(&client->ssl);
mbedtls_ssl_config_init(&client->conf);
mbedtls_x509_crt_init(&client->cacert);
mbedtls_x509_crt_init(&client->clicert);
mbedtls_pk_init(&client->pkey);
mbedtls_entropy_init(&client->entropy);
mbedtls_ctr_drbg_init(&client->ctr_drbg);
/* 乱数生成器の設定 */
mbedtls_ctr_drbg_seed(&client->ctr_drbg,
mbedtls_entropy_func, &client->entropy,
(const unsigned char *)"device-001", 10);
/* CA 証明書の読み込み */
mbedtls_x509_crt_parse(&client->cacert,
(const unsigned char *)ca_cert_pem,
strlen(ca_cert_pem) + 1);
/* クライアント証明書と鍵の読み込み */
mbedtls_x509_crt_parse(&client->clicert,
(const unsigned char *)cli_cert_pem,
strlen(cli_cert_pem) + 1);
mbedtls_pk_parse_key(&client->pkey,
(const unsigned char *)cli_key_pem,
strlen(cli_key_pem) + 1,
NULL, 0,
mbedtls_ctr_drbg_random, &client->ctr_drbg);
/* TLS 設定 */
mbedtls_ssl_config_defaults(&client->conf,
MBEDTLS_SSL_IS_CLIENT,
MBEDTLS_SSL_TRANSPORT_STREAM,
MBEDTLS_SSL_PRESET_DEFAULT);
/* TLS 1.3 を優先、1.2 も許可 */
mbedtls_ssl_conf_max_tls_version(&client->conf, MBEDTLS_SSL_VERSION_TLS1_3);
mbedtls_ssl_conf_min_tls_version(&client->conf, MBEDTLS_SSL_VERSION_TLS1_2);
/* CA 証明書を使ってサーバー証明書を検証 */
mbedtls_ssl_conf_authmode(&client->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
mbedtls_ssl_conf_ca_chain(&client->conf, &client->cacert, NULL);
/* クライアント証明書の設定(相互 TLS) */
mbedtls_ssl_conf_own_cert(&client->conf,
&client->clicert, &client->pkey);
mbedtls_ssl_conf_rng(&client->conf,
mbedtls_ctr_drbg_random, &client->ctr_drbg);
return mbedtls_ssl_setup(&client->ssl, &client->conf);
}
int tls_client_connect(tls_client_t *client,
const char *host, const char *port) {
/* TCP 接続 */
mbedtls_net_connect(&client->server_fd, host, port,
MBEDTLS_NET_PROTO_TCP);
mbedtls_ssl_set_bio(&client->ssl, &client->server_fd,
mbedtls_net_send, mbedtls_net_recv, NULL);
mbedtls_ssl_set_hostname(&client->ssl, host);
/* TLS ハンドシェイク */
int ret;
do {
ret = mbedtls_ssl_handshake(&client->ssl);
} while (ret == MBEDTLS_ERR_SSL_WANT_READ ||
ret == MBEDTLS_ERR_SSL_WANT_WRITE);
return ret;
}
ESP32 での TLS 接続(ESP-IDF)
#include "esp_tls.h"
/* 外部に定義された証明書(PEM 形式) */
extern const uint8_t server_cert_pem_start[] asm("_binary_server_cert_pem_start");
extern const uint8_t client_cert_pem_start[] asm("_binary_client_cert_pem_start");
extern const uint8_t client_key_pem_start[] asm("_binary_client_key_pem_start");
esp_tls_cfg_t cfg = {
.cacert_buf = server_cert_pem_start,
.cacert_bytes = server_cert_pem_end - server_cert_pem_start,
.clientcert_buf = client_cert_pem_start,
.clientcert_bytes = client_cert_pem_end - client_cert_pem_start,
.clientkey_buf = client_key_pem_start,
.clientkey_bytes = client_key_pem_end - client_key_pem_start,
};
esp_tls_t *tls = esp_tls_conn_http_new("https://api.example.com", &cfg);
用途・ユースケース
MQTTS(MQTT over TLS)
IoT デバイスから AWS IoT Core や Eclipse Mosquitto へのデータ送信に広く使われる。ポート 8883 で TLS を使い、X.509 証明書によるデバイス認証を行う。
HTTPS(REST API)
デバイスからクラウド API を呼び出す際の標準的な通信保護手段。OTA アップデートのダウンロードも HTTPS 経由が一般的。
CoAP over DTLS(coaps://)
バッテリー駆動の省電力センサーデバイスで CoAP を使う場合、UDP 上で DTLS によりセキュリティを確保する。LwM2M(Lightweight M2M)プロトコルでは DTLS が標準の通信保護手段。
VPN
OpenVPN、WireGuard などの VPN でも TLS(または TLS ベースの技術)が使われており、産業 IoT のゲートウェイとサーバー間の安全なトンネル確立に使用される。
実装・開発のポイント
メモリ使用量の最適化
組み込みでは TLS のメモリ使用量が問題になることがある。Mbed TLS は細かな設定で不要な機能を除去できる。
/* mbedtls_config.h での最小構成例(IoT向け) */
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED /* ECDHE-ECDSA のみ */
#define MBEDTLS_AES_C
#define MBEDTLS_GCM_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
/* 不要なものを削除 → RAM 使用量を大幅削減 */
| 設定 | RAM 使用量(目安) |
|---|---|
| フルビルド | 50〜100 KB |
| IoT 最小構成 | 10〜20 KB |
| wolfSSL 最小構成 | 20〜40 KB |
証明書の更新(証明書ピンニングの注意)
証明書ピンニング(特定の証明書/公開鍵のみ信頼)を使うと MitM 攻撃への耐性が上がるが、証明書更新時にデバイスのアップデートが必要になる。IoT では CA ピンニング(特定 CA のみ信頼)が実用的なバランスである。
デバッグ時の注意
開発環境では証明書検証を無効化したくなることがあるが、本番環境への誤移植を防ぐためフラグで明確に区分する。
#ifdef DEBUG_SKIP_TLS_VERIFY
/* 開発時のみ: 証明書検証スキップ(本番コードでは絶対に使わない) */
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_NONE);
#warning "TLS certificate verification is DISABLED!"
#else
mbedtls_ssl_conf_authmode(&conf, MBEDTLS_SSL_VERIFY_REQUIRED);
#endif
他技術との比較
| プロトコル | トランスポート | 用途 | IoT 適性 |
|---|---|---|---|
| TLS 1.3 | TCP | HTTPS、MQTTS、一般通信 | 中(TCP オーバーヘッドあり) |
| DTLS 1.3 | UDP | CoAP、SRTP、低遅延通信 | 高(UDP で軽量) |
| QUIC | UDP + TLS 1.3 | HTTP/3 | 中(まだ組み込み普及は少ない) |
| IPsec | IP レイヤー | VPN、L3 暗号化 | 低(複雑、リソース大) |
| SSH | TCP | リモートシェル、ファイル転送 | 低(デバイス間通信には過剰) |
TLS/DTLS はMQTT、HTTP、CoAP など多くのアプリケーションプロトコルの下位で動作し、IoT セキュリティの通信層の要(かなめ)となる。PKI/証明書と組み合わせることで、デバイス認証と通信暗号化の両方が実現できる。