Let's Encrypt の証明書をブラウザ上で簡単取得 (dns-01 / ECDSA もあるよ)

(2024/02/11 追記)
退避先のドメイン名「sslnow.cf」も利用できなくなったため、独自ドメイン配下の「[https://sslnow.16mhz.net/](https://sslnow.16mhz.net/)」に移しました。。
「sslnow.ml」「sslnow.cf」でブラウザのlocalStorageに保存されていた既存の署名鍵は利用できないため、再度Let's Encrypt への署名鍵の登録が必要となります。

(2023/07/23 追記)
これまで利用していたドメイン名「sslnow.ml」が利用できなくなったため、いったん「[https://sslnow.cf/](https://sslnow.cf/)」に退避しています。

(2019/11/22 追記)
ACMEv2 に対応しました。
これに伴いワイルドカード証明書にも対応しました。

# はじめに
無料で SSL 証明書を取得できる [Let's Encrypt](https://letsencrypt.org/) も 2016/04/12 にベータが外れ、[当初からの証明書発行数は約 600 万枚](https://letsencrypt.org/stats/)となるなど、かなり普及が進んでいるようです。
前回の[「ブラウザで動く Let's Encrypt クライアントを作ってみた」](http://qiita.com/tappie/items/e210c646ba10974f9604)ではブラウザ上で証明書を取得できる[「SSLなう! (https://sslnow.16mhz.net/)」](https://sslnow.16mhz.net/)を作成しましたが、今回はさらに dns-01 と ECDSA に対応させてみました。

# 「SSLなう!」とは
[「SSLなう! (https://sslnow.16mhz.net/)」](https://sslnow.16mhz.net/)はブラウザ上で動作する Let's Encrypt クライアントです。
特徴は以下のような感じです。

- 証明書発行までほぼブラウザのみで可能 (ドメイン名所有確認のためのコンテンツ設置 / DNS レコード設定を除く)
 - 最近のブラウザが動作すればどの OS でも OK
- SAN (Subject Alternative Name) による複数ドメイン名への証明書発行に対応
- サーバ秘密鍵はブラウザ上での生成と既存鍵の持ち込みの両方に対応
- サーバ秘密鍵は RSA と ECDSA に対応
- ドメイン名所有確認方法として http-01 と dns-01 に対応
- 証明書発行のための通信はすべて ブラウザ と Let's Encrypt サーバ の間で直接行われる
- 証明書申請用の署名鍵・連絡先はブラウザの localStorage に保存

ACME クライアントとしての詳細な動作については前回の記事[「ブラウザで動く Let's Encrypt クライアントを作ってみた」](http://qiita.com/tappie/items/e210c646ba10974f9604)を参照してください。

# Let's Encrypt のドメイン名所有確認方法
Let's Encrypt で証明書を発行するためにはドメイン名の所有確認が必要です。
前回の「SSLなう!」はドメイン名の所有確認方法として「http-01」のみに対応していましたが、今回「dns-01」にも対応しました。

## http-01
「http-01」では Let's Encrypt サーバからトークンを取得し、このトークンを含むファイルを対象ドメイン名の web サーバ上の特定のディレクトリに置くことでドメイン名の所有確認を行います。

たとえば「example.com」の証明書を発行する場合、
`http://example.com/.well-known/acme-challenge/(取得したトークン)`
という URL にファイルを設置する必要があります。

## dns-01
これに対して「dns-01」では、Let's Encrypt サーバから取得したトークンを、対象ドメイン名の権威 DNS サーバに登録することで所有確認を行います。

たとえば「example.com」の証明書を発行する場合、DNS サーバに
`_acme-challenge.example.com. IN TXT "(取得したトークンを元に生成したハッシュ値)"`
というレコードを設定します。

「dns-01」の「http-01」に対するメリットは、web サーバ上でのファイル設置等が不要 (というかそもそも web サーバが不要) なことです。
このため、

- web サーバのアクセス元を限定している
- ロードバランサ構成で負荷分散先 web サーバの数が多い
- 証明書のインストール先とコンテンツの配置先の管理者が異なる (SSL アクセラレータなど)
- メールサーバなど web サーバ以外のサーバで証明書を利用したい

などの場合にとっても便利です。

# Let's Encrypt で使用できる暗号鍵
Let's Encrypt では HTTPS の暗号鍵として一般的に利用されている RSA に対応していましたが、これに加えて楕円曲線暗号を使用した ECDSA にも対応するようになりました。

前回の「SSLなう!」でも RSA のみに対応していましたが、今回は ECDSA にも対応しました。

RSA / ECDSA それぞれにメリット・デメリットがあるようですが、暗号については疎いため詳細は省きます。

# 証明書取得の流れ
それでは実際に [SSLなう! (https://sslnow.16mhz.net/)](https://sslnow.16mhz.net/) で証明書を取得してみましょう！
例として「test.sslnow.ml」と「www.test.sslnow.ml」の証明書を ECDSA を使用して取得してみます。

## 1. Let's Encrypt へ登録 (初回のみ)
まずは Let's Encrypt に証明書発行用の署名鍵を登録します。
![01_reg.png](https://qiita-image-store.s3.amazonaws.com/0/103430/39343bcf-1ff0-2c83-cbd5-96394e823f8f.png)
メールアドレスを入力し、Let's Encrypt の利用規約に同意したらチェックボックスにチェックを入れ、登録ボタンを押します。
「SSLなう!」では RSA 2048 bits の署名鍵を自動的に生成し、登録に成功したらブラウザの localStorage に保存します。
次回以降はブラウザから自動的に署名鍵が読み込まれるため、登録のステップは不要になります。
※この署名鍵は証明書発行の際に Let's Encrypt サーバとの通信に使用されるものであり、発行される証明書の内容には影響しません。

## 2. チャレンジトークンの取得
ドメイン名の所有確認のためにトークンを取得します。
![02_token.png](https://qiita-image-store.s3.amazonaws.com/0/103430/afb07af9-9399-0447-cc48-1323797b7324.png)
証明書の対象にしたいドメイン名を入力します。
複数のドメイン名に対する証明書を発行したい場合は「追加」ボタンで入力欄を増やして入力します。
入力したら「トークンを取得」ボタンを押してトークンを取得します。

## 3. ドメイン名の所有確認
トークンを取得できたら http-01 または dns-01 のいずれかの方法でドメイン名の所有確認を行います。

### http-01 の場合
![03_http.png](https://qiita-image-store.s3.amazonaws.com/0/103430/9f6f4f20-72c5-d9a1-7f1a-d145210821f5.png)
左側に表示された URL に、右側の内容のファイルを設置します。
ドメイン名が複数の場合はそれぞれのドメイン名に対してファイルを設置します。
すべて設置ができたら「確認」ボタンを押して Let's Encrypt 側からの確認を要求します。

### dns-01 の場合
![03_dns.png](https://qiita-image-store.s3.amazonaws.com/0/103430/0b0ba146-fda9-1b0e-4da8-686c44bd6faf.png)
対象ドメイン名を管理する権威 DNS サーバ上で、左側に表示された FQDN の TXT レコードとして右側の内容を設定します。
ドメイン名が複数の場合はそれぞれのドメイン名に対してレコードを設定します。
すべて設定ができたら「確認」ボタンを押して Let's Encrypt 側からの確認を要求します。

※ ドメイン登録事業者の提供する DNS サーバを利用している場合などで、web 上のコントロールパネルでレコードの設定を行ってから実際に権威 DNS サーバに反映されるまでにタイムラグがある場合があります。
権威 DNS サーバに設定が反映されたことを確認してから「確認」ボタンを押してください。

## 4. 証明書発行申請
無事にドメイン名の所有確認ができたらいよいよ証明書の発行を申請します。
![04_ecdsa.png](https://qiita-image-store.s3.amazonaws.com/0/103430/e9cf2a79-7d66-e81b-ccb4-6fe6c9c661dc.png "ECDSA は鍵長が短くすみます")
「RSA」か「ECDSA」のどちらかを選んで使用する秘密鍵を入力します。
サーバなどで事前に生成しておいた鍵をコピペしても良いですし、ここで「生成」ボタンを押して生成しても構いません。
秘密鍵を入力したら「証明書発行申請」ボタンを押して証明書の発行を申請します。

## 5. 発行された証明書
申請内容に問題が無ければ発行された証明書が表示されます。
![05_cert.png](https://qiita-image-store.s3.amazonaws.com/0/103430/6c0befd4-f432-c8ba-b70b-21d2be0c58b0.png)
あとは 4. の秘密鍵と 5. で得られた証明書をサーバ等に deploy すれば完了です。
お疲れさまでした:relieved:

ちなみに今回発行された証明書の中身を openssl コマンドで確認すると以下のようになっています。
ECDSA の公開鍵が含まれており、指定した 2 つのドメイン名が SAN (Subject Alternative Name) に含まれています。

```shell-session
$ cat cert.pem |openssl x509 -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            03:e1:c1:50:48:d7:e8:2b:83:f2:c6:a9:d5:fb:28:64:6a:86
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3
        Validity
            Not Before: Jul  8 22:30:00 2016 GMT
            Not After : Oct  6 22:30:00 2016 GMT
        Subject: CN=test.sslnow.ml
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                    04:a2:ab:20:73:d8:80:8e:ad:b1:60:84:5d:d0:e2:
                    2d:14:40:fd:91:0b:47:bd:63:f7:6d:b7:19:b9:80:
                    84:dd:54:66:40:e6:57:3e:af:4a:53:32:3d:16:bf:
                    d1:47:64:e1:97:a2:f5:1d:12:13:74:8e:14:b0:32:
                    92:4c:dc:ee:fc
                ASN1 OID: prime256v1
        X509v3 extensions:
            X509v3 Key Usage: critical
                Digital Signature
            X509v3 Extended Key Usage:
                TLS Web Server Authentication, TLS Web Client Authentication
            X509v3 Basic Constraints: critical
                CA:FALSE
            X509v3 Subject Key Identifier:
                28:B0:F0:55:E2:5C:2D:6B:7C:B5:98:39:D5:AA:C9:56:9E:BB:43:31
            X509v3 Authority Key Identifier:
                keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1

            Authority Information Access:
                OCSP - URI:http://ocsp.int-x3.letsencrypt.org/
                CA Issuers - URI:http://cert.int-x3.letsencrypt.org/

            X509v3 Subject Alternative Name:
                DNS:test.sslnow.ml, DNS:www.test.sslnow.ml
            X509v3 Certificate Policies:
                Policy: 2.23.140.1.2.1
                Policy: 1.3.6.1.4.1.44947.1.1.1
                  CPS: http://cps.letsencrypt.org
                  User Notice:
                    Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/

    Signature Algorithm: sha256WithRSAEncryption
         38:3b:83:cd:24:f8:c7:6a:f5:5c:8e:67:2c:32:86:29:bd:f4:
         0b:f4:6c:f1:ae:6e:c0:6e:56:6c:97:c0:cf:9a:61:00:bd:b8:
         db:07:e4:72:3e:eb:81:36:c7:e0:f1:44:17:ac:ed:2b:ef:42:
         f0:41:27:ff:5d:64:88:18:91:62:08:f7:b1:ea:5e:60:01:58:
         61:51:cc:99:00:13:33:1f:08:2f:8a:9d:bf:0a:ae:a9:0e:27:
         c9:7f:8d:f4:f0:cf:75:6f:43:f7:ea:0d:46:21:f0:1f:a0:c7:
         a6:28:56:ce:ec:82:38:df:0f:58:13:9e:e4:69:f2:a9:3c:98:
         3c:0e:ea:ef:78:3b:06:1c:0a:e8:43:99:f4:5c:56:0d:2b:4a:
         43:eb:32:bb:a8:ad:2a:fe:d4:65:f8:76:01:ea:1e:5c:c3:8f:
         22:b0:56:5c:82:ac:32:df:68:6c:82:35:be:68:a9:c5:f4:9d:
         78:c7:00:2b:21:52:65:31:fc:62:3b:40:f6:c3:d5:59:83:0e:
         25:3e:af:a5:f6:d4:81:91:28:ee:3a:4c:4c:6b:7b:9c:15:4c:
         10:d4:cd:fb:6a:af:10:53:da:6c:49:70:e3:05:a0:6f:04:82:
         21:7e:d8:28:5b:3f:6e:5d:22:ab:39:2f:6f:4e:fa:d8:50:cf:
         fe:40:22:82
```

# 謝辞・参考文献
「SSLなう!」は [jsrsasign](https://kjur.github.io/jsrsasign/) を使用しています。
素晴らしいライブラリを公開いただきありがとうございます。

その他参考にしたもの・利用したものなど

- [Let's Encrypt - Free SSL/TLS Certificates](https://letsencrypt.org/)
- [ACME-WG 公式](https://github.com/ietf-wg-acme/acme)
- [Let's Encrypt & ACME Overview (hbstyle-2015-1112) // Speaker Deck](https://speakerdeck.com/rrreeeyyy/lets-encrypt-and-acme-overview-hbstyle-2015-1112)
- [jsrsasign - cryptography library in JavaScript](https://kjur.github.io/jsrsasign/)
- [Get HTTPS for free!](https://gethttpsforfree.com/)
- [lukas2511/letsencrypt.sh: letsencrypt/acme client implemented as a shell-script](https://github.com/lukas2511/letsencrypt.sh)
- [Honoka - 日本語も美しく表示できるBootstrapテーマ](http://honokak.osaka/)

# おわりに
基本的に自分のために作ったものではありますが、気軽に使ってもらえればうれしいです。

もともと Let's Encrypt は証明書の更新を自動化することも目的なので、「SSLなう!」のように毎回手動での作業が必要となるものは本来の想定とは異なるのですが、Let's Encrypt を手軽に試してみる手段としては悪くないのでは、と思っています。

また一時期「SSLなう!」自体の証明書の期限を切らしてしまっていてすみませんでした… :sob: 
というわけで「SSLなう!」で試した後は他のスクリプトなどで更新を自動化しておくのが吉です :sunglasses: 

それではみなさん良い週末を :relaxed: