Linux
Apache
openssl
fedora
letsencrypt

Let's Encrypt の証明書をECDSA(ECC: 楕円曲線暗号)で作成する

More than 3 years have passed since last update.


ECDSA で証明書を作成する

特に何も指定しない状態ではRSA 2048 bit 長の公開鍵ペアで証明書が作成されます。

そこを、ECDSA で作成するようにしてみます。

ECDSA で証明書を作成することで、RSA よりも短い鍵長でより高い暗号強度が得られると言われています。


構成

今回検証する環境は次のとおりです。


  • 構成

    OS
    Fedora 23

    OpenSSL
    1.0.2f

    Apache
    2.4.18



ECDSA で公開鍵ペア(CSR) を作成する

openssl コマンドを使用してECDSA なCSR(証明書要求) を作成します。

--csr フラグが追加され、こちら側で作成したCSR を指定できるようになりました。

そこに今回作成したECDSA なCSR を指定します。

それでは、ECDSA なCSR を作成してみましょう。

今回は/etc/letsencrypt/self ディレクトリを独自に作成して、そこにECDSA な秘密鍵と証明書を作成していきます。


格納用ディレクトリの作成

# mkdir -p /etc/letsencrypt/self/foo.example.com

# cd /etc/letsencrypt/self/foo.example.com

openssl コマンドで使える楕円曲線を確認してみます。


使える楕円曲線のリスト

# openssl version

OpenSSL 1.0.2f-fips 28 Jan 2016
# openssl ecparam -list_curves
secp256k1 : SECG curve over a 256 bit prime field
secp384r1 : NIST/SECG curve over a 384 bit prime field
secp521r1 : NIST/SECG curve over a 521 bit prime field
prime256v1: X9.62/SECG curve over a 256 bit prime field

今回は比較的、証明書発行機関でも使われているprime256v1 でCSR を作成してみます。


CSRを作成する

# openssl ecparam -out private_prime256v1.key -name prime256v1 -genkey

# openssl req -new -sha256 -key private_prime256v1.key -subj "/CN=foo.example.com" -reqexts SAN -outform der -out csr_prime256v1.der -config <(
cat <<-EOF
[req]
distinguished_name = dn
[dn]
[SAN]
subjectAltName = DNS:foo.example.com
EOF
)

CSR を作成したら、CSR に対して署名を行ってもらい、証明書を作成します。


letsencryptコマンドで証明書を作成する

# cd /root/letsencrypt

# ./letsencrypt-auto certonly --webroot --renew-by-default --agree-tos -w /var/www/html --csr /etc/letsencrypt/self/foo.example.com/csr_prime256v1.der
Checking for new version...
Requesting root privileges to run letsencrypt...
/root/.local/share/letsencrypt/bin/letsencrypt --no-self-upgrade certonly --webroot --renew-by-default --agree-tos -w /var/www/html --csr /etc/letsencrypt/self/foo.example.com/csr_prime256v1.der

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/root/letsencrypt/0001_chain.pem. Your cert will expire on
YYYY-MM-DD. To obtain a new version of the certificate in the
future, simply run Let's Encrypt again.
- If you like Let's Encrypt, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le


letsencrypt コマンドの実行が完了すると、カレントディレクトリに以下のファイルが作成されます。


作成されるファイル

0000_cert.pem           # サーバ証明書

0000_chain.pem # 中間証明書
0001_chain.pem # サーバ証明書と中間証明書の連結ファイル

これらのファイルを/etc/letsencrypt/self/foo.example.com ディレクトリに移動し、それぞれファイル名をcert.pem, chain.pem, fullchain.pem というファイル名に変更します。


ファイルの移動

# mv 0000_cert.pem /etc/letsencrypt/self/foo.example.com/cert.pem

# mv 0000_chain.pem /etc/letsencrypt/self/foo.example.com/chain.pem
# mv 0001_chain.pem /etc/letsencrypt/self/foo.example.com/fullchain.pem

以上で証明書の取得は完了です。


暗号化スイートの決定

今回作成した証明書で使用できる暗号化スイートとしては、現在使用しているopenssl 1.0.2f によると...


opensslciphers

# openssl ciphers -v 'ECDSA'

ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1
ECDHE-ECDSA-RC4-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=RC4(128) Mac=SHA1
ECDHE-ECDSA-DES-CBC3-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=3DES(168) Mac=SHA1
ECDHE-ECDSA-NULL-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=None Mac=SHA1

のようです。

ここではさらにデータの暗号化方式としてNone, RC43DES が使われている暗号化スイートを除いていきます。

# openssl ciphers -v 'ECDSA !eNULL !3DES !RC4'

ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA384
ECDHE-ECDSA-AES256-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(256) Mac=SHA1
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA256
ECDHE-ECDSA-AES128-SHA SSLv3 Kx=ECDH Au=ECDSA Enc=AES(128) Mac=SHA1

上記に表示されているものを今回採用することにします。

現段階ではECDHE-ECDSA な鍵交換 + 認証 しかないので、フィルタの書き方をECDSA からECDHE+ECDSA に変えておきます。


ECDHE+ECDSA追加

openssl ciphers -v 'ECDHE+ECDSA !eNULL !3DES !RC4'


また、保険の意味も込めて除外する暗号化スイートのフィルタをもっと追加しておきます。

結果以下のようなフィルタ定義を採用することとします。


除外リストの追加

openssl ciphers -v 'ECDHE+ECDSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4'


今回、Apache を使って設定をしていきますが、このフィルタ定義はnginx でも使えます。


ひとりごと1: RSA な証明書が対応している暗号化スイートについて

RSA な証明書が対応している暗号化スイートを確認する場合はopenssl ciphers -v 'aRSA' を実行してみてください。

こちらのほうが、だいぶ多くの暗号化スイートの選択肢があることがわかるはずです。

そのため、古いクライアントがあなたのWeb サイトにアクセスする可能性がある場合、そのクライアントはECDSA な暗号化スイートに対応しいない可能性もある点に注意してください。


ひとりごと2:

暗号化スイートとして、より強固に、TLS1.2 系のみを使用したい場合は、メッセージ認証コード(Mac) としてSHA1 を除去するようにしてください。

フィルタの定義例としては、例えば次のようになります。


除外リストの追加

openssl ciphers -v 'ECDHE+ECDSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4 !SHA1'



ひとりごと3: Enc=None な暗号化スイート

本記事の調べごとをしていて知ったことですが、通信内容を暗号化しない通信方式も立派なSSL/TLS 通信なんですね。しかもTLS1.2 対応だしTLS1.3 の草稿にも含まれています!

「ログ見ればちゃんとTLS1.2 でやっているし安心安心〜(´ー)y-~~」とか余裕こいていると、痛い目見そうですね…。

Enc=None` な暗号化スイートは(あまり用途は思いつきませんが)、通信の暗号化は行わないが接続先の認証はしっかり行うようなケースで使用されるようです。


Web サーバへの設定

細かい手順については割愛しますが、例えばApache を使用している場合、httpd.conf の適切な場所に次のように設定してください。


SSL/TLS周りの設定例

<VirtualHost *:443>

ServerName foo.example.com

......
SSLEngine on
SSLProtocol all -SSLv2 -SSLv3
SSLHonorCipherOrder on
SSLCipherSuite "ECDHE+ECDSA !aNULL !eNULL !LOW !3DES !MD5 !EXP !PSK !SRP !DSS !RC4"

## あなたのサイトが常時SSL (http -> https にリダイレクトする)な設定になっている場合、
## 任意でHSTS (HTTP Strict Transport Security)の設定も追加しておいてください。
# Header set Strict-Transport-Security "max-age=315360000; includeSubDomains"

SSLCertificateFile /etc/letsencrypt/self/foo.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/self/foo.example.com/private_prime256v1.key

......
</VirtualHost>


設定ファイルを書き換えたら、Apache をreload させます。


Apachereload

# systemctl reload httpd



確認

Web サーバに証明書を設定したら、openssl コマンドで確認してみましょう。


openssl

# openssl s_client -servername foo.example.com -connect foo.example.com:443 < <(echo QUIT)

......
New, TLSv1/SSLv3, Cipher is ECDHE-ECDSA-AES256-GCM-SHA384
Server public key is 256 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-ECDSA-AES256-GCM-SHA384
......
Verify return code: 0 (ok)
......

上記の出力結果を確認するとECDHE-ECDSA-AES256-GCM-SHA384 な暗号化スイートが使われて、署名検証にも成功していることがわかります。

上記のような結果になれば成功です。


その他

Letsencrypt 自体がまだ開発段階にあるので、おそらくここで実施した手順は今後、まだまだ変わっていくと思います。

今回、prime256v1 で公開鍵ペアを作成しましたが、secp256k1, secp384r1, secp521r1 なものについては未検証。


参考