l0w.dev

Let's Encrypt の ECDSA 証明書ではまった話


普段から Lego というソフトで Let’s Encrypt にサーバ証明書を要求して使っているのですが、 Lego はデフォルトで ECDSA の証明書を要求するようになっていて (-k ec384)、これがたまに問題を起こしていました。

昨日もそれにひっかかったあげく、さらに深入りして時間を浪費してしまったので、ブログの記事にしておきたいと思います。

Exchange と ECDSA

今回ひっかかったのは 1 年以上前に構築して使っているメールサーバ (Postfix) の StartTLS に使うサーバ証明書で、Lego と cron による自動更新を続けて特にトラブルなく使えていたものです。

昨日 Exchange Online のとあるテナントをいじって Office 365 → Postfix の送信コネクタ (TLS 接続) を作ったところ、 451 4.4.0 Security status IllegalMessage という接続エラーが出てうまくいきませんでした。

そして Postfix 側では次のようなログが残されていました。

Nov 29 20:20:16 mail-000 postfix/smtpd[16423]: connect from mail-pu1apc01lp2056.outbound.protection.outlook.com[104.47.126.56]
Nov 29 20:20:16 mail-000 postfix/smtpd[16423]: SSL_accept error from mail-pu1apc01lp2056.outbound.protection.outlook.com[104.47.126.56]: -1
Nov 29 20:20:16 mail-000 postfix/smtpd[16423]: warning: TLS library problem: error:1408A0C1:SSL routines:ssl3_get_client_hello:no shared cipher:s3_srvr.c:1417:
Nov 29 20:20:16 mail-000 postfix/smtpd[16423]: lost connection after STARTTLS from mail-pu1apc01lp2056.outbound.protection.outlook.com[104.47.126.56]
Nov 29 20:20:16 mail-000 postfix/smtpd[16423]: disconnect from mail-pu1apc01lp2056.outbound.protection.outlook.com[104.47.126.56] ehlo=1 starttls=0/1 commands=1/2

原因がわかりづらいエラーメッセージですが、適当に検索してみると類似の症例が見つかります。

これを見て、Office 365 の Exchange Server は Lego が要求した ECDSA の証明書に対応していないに違いない、 と早々に結論して RSA の証明書に切り替えて終わらせようとしていたのですが、 では実際に Exchange はどういう暗号をサポートしているのだろうと気になったので、調べてみました。

原因の調査

それには TLS のハンドシェイクで Exchange から送られてくる Client Hello の中身を見る必要があるのですが、 Postfix のデバッグログ機能ではペイロードの 16 進ダンプしか表示できないようなので、 結局 tshark を使うことになりました。

以下が Exchange が送ってきた Client Hello と、それに対して Postfix が返した Alert(失敗) です。

Secure Sockets Layer
    TLSv1.2 Record Layer: Handshake Protocol: Client Hello
        Content Type: Handshake (22)
        Version: TLS 1.2 (0x0303)
        Length: 346
        Handshake Protocol: Client Hello
            Handshake Type: Client Hello (1)
            Length: 342
            Version: TLS 1.2 (0x0303)
            Random: 5de27a6ca121e22b2abd6df1ee9ac689fe51042ccbfc99bc...
                GMT Unix Time: Nov 30, 2019 23:19:24.000000000 JST
                Random Bytes: a121e22b2abd6df1ee9ac689fe51042ccbfc99bc0beea610...
            Session ID Length: 0
            Cipher Suites Length: 32
            Cipher Suites (16 suites)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 (0xc02c)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 (0xc02b)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (0xc030)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (0xc02f)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 (0xc024)
                Cipher Suite: TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 (0xc023)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (0xc028)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (0xc027)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (0xc014)
                Cipher Suite: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (0xc013)
                Cipher Suite: TLS_RSA_WITH_AES_256_GCM_SHA384 (0x009d)
                Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256 (0x009c)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA256 (0x003d)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA256 (0x003c)
                Cipher Suite: TLS_RSA_WITH_AES_256_CBC_SHA (0x0035)
                Cipher Suite: TLS_RSA_WITH_AES_128_CBC_SHA (0x002f)
            Compression Methods Length: 1
            Compression Methods (1 method)
                Compression Method: null (0)
            Extensions Length: 269
            Extension: supported_groups (len=4)
                Type: supported_groups (10)
                Length: 4
                Supported Groups List Length: 2
                Supported Groups (1 group)
                    Supported Group: secp256r1 (0x0017)
            Extension: ec_point_formats (len=2)
                Type: ec_point_formats (11)
                Length: 2
                EC point formats Length: 1
                Elliptic curves point formats (1)
                    EC point format: uncompressed (0)
            Extension: signature_algorithms (len=20)
                Type: signature_algorithms (13)
                Length: 20
                Signature Hash Algorithms Length: 18
                Signature Hash Algorithms (9 algorithms)
                    Signature Algorithm: rsa_pkcs1_sha256 (0x0401)
                        Signature Hash Algorithm Hash: SHA256 (4)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: rsa_pkcs1_sha384 (0x0501)
                        Signature Hash Algorithm Hash: SHA384 (5)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: rsa_pkcs1_sha1 (0x0201)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: ecdsa_secp256r1_sha256 (0x0403)
                        Signature Hash Algorithm Hash: SHA256 (4)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: ecdsa_secp384r1_sha384 (0x0503)
                        Signature Hash Algorithm Hash: SHA384 (5)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: ecdsa_sha1 (0x0203)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: ECDSA (3)
                    Signature Algorithm: SHA1 DSA (0x0202)
                        Signature Hash Algorithm Hash: SHA1 (2)
                        Signature Hash Algorithm Signature: DSA (2)
                    Signature Algorithm: rsa_pkcs1_sha512 (0x0601)
                        Signature Hash Algorithm Hash: SHA512 (6)
                        Signature Hash Algorithm Signature: RSA (1)
                    Signature Algorithm: ecdsa_secp521r1_sha512 (0x0603)
                        Signature Hash Algorithm Hash: SHA512 (6)
                        Signature Hash Algorithm Signature: ECDSA (3)
            Extension: SessionTicket TLS (len=218)
                Type: SessionTicket TLS (35)
                Length: 218
                Data (218 bytes)
            Extension: extended_master_secret (len=0)
                Type: extended_master_secret (23)
                Length: 0
            Extension: renegotiation_info (len=1)
                Type: renegotiation_info (65281)
                Length: 1
                Renegotiation Info extension
                    Renegotiation info extension length: 0

Secure Sockets Layer
    TLSv1.2 Record Layer: Alert (Level: Fatal, Description: Handshake Failure)
        Content Type: Alert (21)
        Version: TLS 1.2 (0x0303)
        Length: 2
        Alert Message
            Level: Fatal (2)
            Description: Handshake Failure (40)

ご覧の通り、暗号スイートには ECDSA のものをひととおり対応しているように見えます。 ではなにがダメだったのかというと、 それは楕円曲線群が secp256r1 ひとつしか使えないことだったようです。

            Extension: supported_groups (len=4)
                Type: supported_groups (10)
                Length: 4
                Supported Groups List Length: 2
                Supported Groups (1 group)
                    Supported Group: secp256r1 (0x0017)

そこで Lego のデフォルトである secp384r1 (-k ec384) ではなく secp256r1 (-k ec256) で証明書を作り直してみたところ、 無事に Exchange Online の送信コネクタで Postfix に TLS 接続することができるようになりました。

ということで、実際には Exchange Online は ECDSA をサポートしており、 ECDSA をあきらめて RSA にしなくてもよいことがわかりました。 もっとも、ECDSA 全般に対応していないサーバやクライアントはまだあるみたいなので、 結局のところサーバ証明書は RSA で作る (-k rsa4096) のが一番安全なのかもしれません。

その他の ECDSA トラブル事例

これより前に遭遇した ECDSA がらみのトラブルとしては、 GitLab Pages の独自ドメインサイトに設定するサーバ証明書が RSA でないと受け付けられないということがありました。 これには自分もコメントしていました。

https://gitlab.com/gitlab-org/gitlab-pages/issues/35#note_177010927

なお、今は ECDSA も対応しているそうです。これは純粋に GitLab Pages の Ruby のコードの問題だったようです。

また先月には Windows Server の ADFS と Lego による ECDSA 証明書ではまっている方がいらっしゃいました。 Windows Server がらみということで、今回と根が同じ問題なのかもしれないと思っています。