セキュリティ

TLS / DTLS

通信路を暗号化するプロトコル。

概要

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.2TLS 1.3
ハンドシェイク RTT2 RTT1 RTT(0-RTT も可能)
前方秘匿性オプション必須(ECDHE/DHE のみ)
廃止されたものRSA 鍵交換、CBC モード、SHA-1、MD5
暗号スイート多数(選定複雑)5 種類に限定(シンプル)
ハンドシェイクの暗号化なしあり(Certificate も暗号化)
セッション再開Session ID/TicketPSK ベース

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 の比較

項目TLSDTLS
トランスポートTCPUDP
パケット損失TCP が再送を保証DTLS レイヤーで再送処理
順序保証TCP が保証DTLS がシーケンス番号管理
ブロッキングあり(Head-of-Line)なし
主な用途HTTPS、MQTT、AMQPCoAP、SRTP、QUIC(前身)
RFCRFC 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.3TCPHTTPS、MQTTS、一般通信中(TCP オーバーヘッドあり)
DTLS 1.3UDPCoAP、SRTP、低遅延通信高(UDP で軽量)
QUICUDP + TLS 1.3HTTP/3中(まだ組み込み普及は少ない)
IPsecIP レイヤーVPN、L3 暗号化低(複雑、リソース大)
SSHTCPリモートシェル、ファイル転送低(デバイス間通信には過剰)

TLS/DTLS はMQTT、HTTP、CoAP など多くのアプリケーションプロトコルの下位で動作し、IoT セキュリティの通信層の要(かなめ)となる。PKI/証明書と組み合わせることで、デバイス認証と通信暗号化の両方が実現できる。

関連用語

参考リンク