してみるテストロゴ
Apache 2.4系でHTTP/2サーバを構築してみるテスト。

ECDSA対応CSRを生成して、Let's Encryptをつかう場合

Let's Encryptは2016年2月11日に、楕円曲線暗号(Elliptic Curve Cryptography:ECC)を使ったECDSA対応のサーバ証明書の発行に対応しました。

暗号の強度(ビット数)にもよりますが、ECCの利点は、ほぼ同じ暗号強度のRSAに比べて「署名の生成」の速度が早いことです。逆に、同一の条件の「署名の検証」はRSAの方が早いです。

TLS通信では、「署名の生成」は、サーバの仕事になります。一方、「署名の検証」はクライアントの仕事になります。

ECDSA対応のサーバ証明書を使うと、サーバ側の「署名の生成」の処理が、かなり軽くなり、クライアント側の「署名の検証」の処理が若干重くなります。

つまり、サーバ側の処理を、軽くできる利点があります。

[2016年2月16日追記]

ECDSAを使うとどれくらいサーバ負荷が減るのか気になるところだと思います。

これについては、サーバ負荷をRSAとECDSAで比較をご覧ください。

サーバ側、クライアント側がそれぞれどれくらいの処理時間を必要とするかは、コチラにも詳しい説明があります。

ほぼ同じ暗号強度と言われる、256ビットのECCと、3072ビットのRSAの比較を見ると、サーバ側の処理時間が、かなり短くなり、クライアント側の処理時間が若干増える感じでしょうか。

全体の処理時間(サーバとクライアントの合計)を見ても、かなり短くなっているので、この暗号強度となれば、ECCを使った方が良さそうです。

Let's EncryptのECDSA対応は、時代を変えるような気がします。

 

当サイトも、早速、ECDSA対応のサーバ証明書を取得してみました。ネゴシエーションのスピードなど、お試しください。

EC証明書1

いまところECDSA対応のサーバ証明書は、letsencrypt-autoだけでは、取得できません。OpenSSLと組み合わせて取得します。(そのうち、letsencrypt-autoだけで、できるようになると思いますが…)

流れとしては、自分でCSRを生成して、Let's Encryptをつかう場合に似ています。

CSRといっても、Let's Encryptが受け入れるCSRは、一般に証明書発行機関に提出するときに使用するPEM形式(Base64符号化)ではなく、バイナリ形式のDER形式です。

 

[2016年12月13日追記]

このページで説明している、letsencrypt-autoとは別のACMEエージェントである、dehydratedコマンドの説明を追加しました。

こちらのほうが、移植性が高く、ECDSA対応証明書も簡単に取れます。

ECDSA対応のDER形式のCSRと、証明書の取得

まずは、作業フォルダを掘ります。今回は、/etc/letsencryptの配下に、der_keysを掘ります。パーミッションを700にしておきます。

# cd /etc/letsencrypt
# mkdir der_key
# chmod 700 der_keys
# ls -la |grep der
drwx------   2 root     root           2  1月 11日  09:51 der_keys

次に楕円曲線暗号(ECC)の鍵ですが、以下の通り生成します。

楕円曲線名は-nameオプションで指定します。どの楕円曲線を使うのかは、議論があるところです。

ビット数が多ければ安心なのですが、ビット数によっては、対応していないOSもあるようです。

お手元のOpenSSLがどの楕円曲線を持っているのかは、以下のコマンドで確認できます。

# /usr/local/ssl/bin/openssl ecparam -list_curves

たくさん出てきますが、たぶんOpenSSLによって差はあまりないと思います。

この中で、Windowsなどの、一般的なブラウザから利用できそうなのは、以下の2つ(少ない!)になります。

prime256v1 (P-256)

secp384r1 (P-384)

ただ、521ビット(secp521r1 (P-521))のものは、 以前は使えていたようですが、こちらの説明の通り、現在、Windowsと、Chromeのサポート対象の楕円曲線から外れているようです。

なお、RSA暗号の鍵長が3072ビットに相当する暗号強度を、楕円曲線暗号では256ビットで得られるといわれています。

Let's Encryptが、楕円曲線名:prime256v1の証明書の出力に対応していることは確認できていますので、今回はこれを使います。

# openssl ecparam -out /etc/letsencrypt/der_keys/example.com/privkey.pem -name prime256v1 -genkey

すると、以下の鍵が生成されます。

#more /etc/letsencrypt/der_keys/example.com/privkey.pem
-----BEGIN EC PARAMETERS-----
楕円曲線名
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
楕円曲線鍵
-----END EC PRIVATE KEY-----

続いて、OpenSSLをたたいて、DER形式で、ECDSA対応のCSRを生成します。

CSRの生成ですが、SHA256の署名(-sha256)はもちろんですが、-subjオプションで、コモンネーム(CN)を指定します。CN以外は、証明書に入りません。

また、-reqexts SAN に加えて、-configオプションで、opensslのconfigにSANの設定も入れておきます。

#  /usr/local/ssl/bin/openssl req -new -key /etc/letsencrypt/der_keys/example.com/privkey.pem -sha256  -nodes  -outform der -out /etc/letsencrypt/der_keys/exapmle.com/csr.der -subj "/CN=example.com"  -reqexts SAN  -config <(
cat <<-EOF
[req]
distinguished_name = dn
[dn]
[SAN]
subjectAltName=DNS:example.com
EOF
)

一応、楕円曲線暗号の鍵になっているか確認します。

# openssl req -in /etc/letsencrypt/der_keys/example.com/csr.der -inform der -text
Certificate Request:
    Data:
        Version: 0 (0x0)
        Subject: CN=example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                (…省略…)
                ASN1 OID: prime256v1
                NIST CURVE: P-256
        Attributes:
        Requested Extensions:
            X509v3 Subject Alternative Name:
                DNS:example.com
    Signature Algorithm: ecdsa-with-SHA256
    (…省略…)
-----BEGIN CERTIFICATE REQUEST-----
(…省略…)
-----END CERTIFICATE REQUEST-----

そして、CSRを提出して証明書をもらいます。

ここでは、これまでのwebroot-ptahオプションではなく、webroot-mapオプションを使っています。

certbotの場合:


# certbot certonly -t -a webroot --webroot-map '{"example.com" : "/var/https/example.com/htdocs/" }' --redirect --csr  /etc/letsencrypt/der/example.com.der --server https://acme-v01.api.letsencrypt.org/directory 

letsencrypt-autoの場合:


# cd /usr/local/letsencrypt
# ./letsencrypt-auto certonly -t -a webroot --webroot-map '{"example.com" : "/var/https/example.com/htdocs/" }' --redirect --csr  /etc/letsencrypt/der/example.com.der --server https://acme-v01.api.letsencrypt.org/directory 

IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /usr/local/letsencrypt/0001_chain.pem. Your cert will
   expire on 2016-XX-XX. 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

と、いつも通りのメッセージが出て証明書ができます。

証明書類はどこにできるかというと、カレントディレクトリに、

# ls -la *.pem 
-rw-r--r--   1 root     root        1818  2月 11日  21:05 0000_cert.pem
-rw-r--r--   1 root     root        1675  2月 11日  21:05 0000_chain.pem
-rw-r--r--   1 root     root        3493  2月 11日  21:05 0001_chain.pem

と、3つのファイルができていればOKです。それぞれ、

0000_cert.pem が、サーバ証明書

0000_chain.pem が、中間証明書

0001_chain.pem が、上記2つを連結したファイル

となります。普通に、letsencrypt-autoを利用した場合と違って、カレントディレクトリに出力されるので、注意して下さい。

また、このファイルが残ったままだと、先頭の数字が重ならないように新たな番号(0002以降)のファイル名で次々生成されます。

そこで、次のコマンドで、先ほど生成したder_keysディレクトリに移動しておきます。

# mv 0000_cert.pem /etc/letsencrypt/der_keys/example.com/cert.pem
# mv 0000_chain.pem /etc/letsencrypt/der_keys/example.com/chain.pem
# mv 0001_chain.pem /etc/letsencrypt/der_keys/example.com/fullchain.pem

さて、本当に、ECDSA対応か見てみましょう。

# cd /etc/letsencrypt/der_keys/examle.com
# /usr/local/ssl/bin/openssl x509 -in cert.pem -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            01:5c:cf:2e:e2:3d:38:41:79:ed:64:52:9a:60:b2:19:be:6c
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X1
        Validity
            Not Before: Feb 11 00:50:00 2016 GMT
            Not After : May 11 00:50:00 2016 GMT
        Subject: CN=example.com
        Subject Public Key Info:
            Public Key Algorithm: id-ecPublicKey
                Public-Key: (256 bit)
                pub:
                (…省略…)
                ASN1 OID: prime256v1
                NIST CURVE: P-256
                (…省略…)

Public Key Algorithm: id-ecPublicKeyとなっていればOKです。

ちなみに、署名アルゴリズム(Signature Algorithm)がsha256WithRSAEncryptionですが、これは、後続の中間証明書との関係なので、気にしなくて良いです。

「署名の検証」は、ECCよりも、RSAの方が速いので、これはこれで、妥当だと思われます。

一応確認すると、中間証明書(chain.pem)は、2048bitのRSA暗号です。

# cd /etc/letsencrypt/der_keys/examle.com
# /usr/local/ssl/bin/openssl x509 -in chain.pem -text
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            98:13:f4:75:13:e5:75:0b:43:e7:43:1e:97:1e:44:bd
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: O=Digital Signature Trust Co., CN=DST Root CA X3
        Validity
            Not Before: Oct 19 22:33:36 2015 GMT
            Not After : Oct 19 22:33:36 2020 GMT
        Subject: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X1
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                (…省略…)

続いて、このサーバ証明書をWebサーバにインストールします。

サーバにECDSAの設定を追加

ECDSA対応の設定の追加については、既に当サイトで説明した、サーバのTLS(SSL)設定の設定が済んでいる前提となります。

まだの方は、こちらを先に済ませておいてください。

まず、さきほど取得して移動したサーバ証明書のパスに、設定を書き換えます。

# vi /usr/local/apache2/conf/extra/httpd-ssl-vhosts.conf
/usr/local/apache2/conf/extra/httpd-ssl-vhosts.conf
<VirtualHost *:443>

	SSLEngine on

	SSLCertificateFile "/etc/letsencrypt/der_keys/example.com/cert.pem"
	SSLCertificateKeyFile "/etc/letsencrypt/der_keys/example.com/privkey.pem"
	SSLCertificateChainFile "/etc/letsencrypt/der_keys/example.com/chain.pem"

	DocumentRoot "/var/https/example.com/htdocs/"
	ServerName example.com:443
	ServerAdmin webmaster@example.com
    
	<Directory /var/https/example.com/htdocs>
		Options -Indexes
		AllowOverride All
		Require all granted
	</Directory>
	<FilesMatch "\.(cgi|shtml|phtml|php)$">
		SSLOptions +StdEnvVars
	</FilesMatch>
    
	ErrorLog "|/usr/local/apache2/bin/rotatelogs -l /var/log/https/example.com/error_log.%Y%m%d 86400"
	CustomLog "|/usr/local/apache2/bin/rotatelogs -l /var/log/https/example.com/access_log.%Y%m%d 86400" combined
	CustomLog "|/usr/local/apache2/bin/rotatelogs -l /var/log/https/example.com/ssl_request_log.%Y%m%d 86400" "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
    
 </VirtualHost>

続いて、暗号スイートに鍵認証のアルゴリズムとして、ECDSAの暗号を含んだものを追加しておきます。

/usr/local/apache2/conf/extra/httpd-ssl.confの一部
…(省略)…
SSLProtocol -All +TLSv1 +TLSv1.1 +TLSv1.2
SSLHonorCipherOrder on
SSLCipherSuite ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-SHA256:DHE-RSA-AES128-SHA256:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
…(省略)…

実際には、証明書の関係から、ECDHE-ECDSAから始まる、の6種類が使われます。

[2016年2月19日追記]:証明書と、暗号スイートの関係

たまに、「RSAと DSSとECDSA に対応しています」とか書いているサイトがありますが、DSSを含む証明書を運用するには、私が知る限り、シマンテック社のマルチアルゴリズム証明書サービス以外の選択肢しか無い気がします。

とおもって、調べてみると、やっぱり、そのサイトの証明書はRSAだけだったりします。

このように、OpenSSLのリストにアルゴリズムの記載があったとしても、それは、Webサーバのソフトウエアが、暗号スイートとして利用できるだけで、実際に使われる鍵認証アルゴリズムは証明書次第です。

つまり、RSAか DSSかECDSAのうち、対応した証明書がインストールされている暗号です。

暗号スイートとしては、それに付随する、鍵交換方式と、共通鍵暗号、署名方式をバリエーションとして選択できます。ここではバリエーションが6種類あると言うことです。

先頭の4つとは別に、後方に加えたECDHE-ECDSA-AES256-SHAとECDHE-ECDSA-AES128-SHAの2つの暗号プロトコルは、SHA-1を利用していますので、若干の不安がありますが、TLSv1.0対応のブラウザとの通信に必要となるので、加えるのを忘れないでください。

apachctl -tで、設定ファイルの間違いが無いこと(Syntax OKが表示されること)を確認の上、下記のように

# /usr/local/apache2/bin/apachctl -t
Syntax OK
# /usr/local/apache2/bin/apachctl stop
# /usr/local/apache2/bin/apachctl start

とします。

最新のChromeブラウザでのECDSA証明書の確認方法
このページの以下の確認方法は、古いバージョン(49以前)の確認方法になります。最新のChromeブラウザ(バージョン50以上)での確認方法はコチラ

Chromeブラウザで確認すると、以下の通り、ECDSAによって、サーバ証明書が確認されていることが示されています。

 

EC証明書1

早速、QUALYS SSL LabsのSSLサーバテストで調べると、、使用する暗号の組み合わせは、 以下の通りです。

EC証明書2

 

表3:Forward Secrecyに対応した楕円曲線暗号プロトコルと、OS/ブラウザの状況
OpenSSLでの名称 暗号スイートOS/ブラウザ
ECDHE-ECDSA-AES256-GCM-SHA384 TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384   Android 4.4 (TLS1.2)
WIndows 10 / Internet Explorer 11 (TLS1.2)
WIndows 10 / Edge (TLS1.2)
OS X 10.11/Safari 9.0 (TLS1.2)
iOS 9 / Safari 9.0 (TLS1.2)
ECDHE-ECDSA-AES256-SHA384 TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384   Windows 7/8/8.1 / Internet Exploler 11 (TLS1.2)
Windows Phone 8.1 Update / Internet Explorer 11 (TLS1.2)
OS X 10.9 / Safari 7 (TLS1.2)
OS X 10.10 / Safari 8 (TLS1.2)
iOS 6~8 / Safari 6~8 (TLS1.2)
ECDHE-ECDSA-AES128-GCM-SHA256TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 Android 5.0 (TLS1.2)
OS X / Chrome 45 (TLS1.2)
OS X / Firefox 41 (TLS1.2)
ECDHE-ECDSA-AES128-SHA256 TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256  Windows Phone 8.1 / Internet Explorer 11 (TLS1.2)
ECDHE-ECDSA-AES256-SHATLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA  Android4.0 ~ Android4.3 (TLS1.0)
Windows Vista / Internet Explorer 7 (TLS1.0)
Windows 7 / Internet Explorer 8-10 (TLS1.0)
Windows Phone 8 / Internet Explorer 9 (TLS1.0)
OS X 10.6 / Safari 5.1 (TLS1.0)
OS X 10.8 / Safari 6.0 (TLS1.0)
ECDHE-ECDSA-AES128-SHATLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA

TLSv1.2対応のブラウザは、ECDSAと一緒に、SHA256以上の署名を行います。

TLSv1.0対応のブラウザは、ECDSAの組み合わせにおいて、署名にとしてSHA-1を利用しているため、若干の心配があります(つまりTLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)。

はやくTLSv1.2がメインになってほしいところです。(HTTP/2はTLSv1.2が必須です!)

 

ちなみに、先ほどの設定から、後方に加えたECDHE-ECDSA-AES256-SHAとECDHE-ECDSA-AES128-SHAの2つ(つまり、TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA やTLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)を除いて、TLSサーバを動かすと2016年2月11日現在、、QUALYS SSL LabsのSSLサーバテストのProtocol Supportが100点(満点)となります。

当サイトのアクセスログの取り方を設定していれば、アクセスログ上は、以下のようになります。

ww.xx.yy.zz "2016-MM-DD hh:mm:ss +0900" TLSv1.2 ECDHE-ECDSA-AES256-GCM-SHA384 26884 15 - GET "/index.html" "" HTTP/2 200 10210 "-"
 "nghttp2/1.7.0"

 

cronによる証明書の再取得(ECDSA編)

いま実行したコマンド+オプションをシェルスクリプトに記載し、cronで、月に1回実行しましょう。Apacheのリスタートも行います。

# vi /usr/local/apache2/bin/cert_ecdsa_refresh.sh
/usr/local/apache2/bin/cert_ecdsa_refresh.sh
#! /bin/sh
/usr/local/ssl/bin/openssl ecparam -out /etc/letsencrypt/der_keys/example.com/privkey.pem -name prime256v1 -genkey
/usr/local/ssl/bin/openssl req -new -key /etc/letsencrypt/der_keys/example.com/privkey.pem -sha256  -nodes  -outform der -out /etc/letsencrypt/der_keys/exapmle.com/csr.der -subj "/CN=example.com"  -reqexts SAN  -config <(
cat <<-EOF
[req]
distinguished_name = dn
[dn]
[SAN]
subjectAltName=DNS:example.com
EOF
)
cd /usr/local/letsencrypt/
./letsencrypt-auto certonly -t -a webroot --webroot-map '{"example.com" : "/var/https/example.com/htdocs/" }' --csr  /etc/letsencrypt/der_keys/example.com/csr.der --server https://acme-v01.api.letsencrypt.org/directory 

#certbotの場合
#certbot certonly -t -a webroot --webroot-map '{"example.com" : "/var/https/example.com/htdocs/" }' --csr  /etc/letsencrypt/der_keys/example.com/csr.der --server https://acme-v01.api.letsencrypt.org/directory 


#証明書が発行されていれば、

if [ -e 0000_cert.pem ]; then

#証明書を/etc/letsencrypt/der_keys/配下にインストール。
#割愛していますが、旧証明書はバックアップした方がいいかも…

	mv 0000_cert.pem /etc/letsencrypt/der_keys/example.com/cert.pem
	mv 0000_chain.pem /etc/letsencrypt/der_keys/example.com/chain.pem
	mv 0001_chain.pem /etc/letsencrypt/der_keys/example.com/fullchain.pem

#リスタート
	/usr/local/apache2/bin/apachectl restart
fi

仕上げに、実行できるようにパーミッションを変えます。

# chmod 700 /usr/local/apache2/bin/cert_ecdsa_refresh.sh

たとえば、毎月15日の午前3時30分に、ECDSA対応の証明書の再取得と、Apacheのリスタートをするとすれば、

# crontab -e
/var/spool/cron/crontabs/root/
30 3 15 * * * /usr/local/apache2/bin/cert_ecdsa_refresh.sh

となります。

NEXT >> HTTP/2の各種設定
[番外編] >> Let's Encryptを使ってqmailのTLSv1.2対応

©Copyrights 2015-2023, non-standard programmer

このサイトは、あくまでも私の個人的体験を、綴ったものです。 軽く参考程度にご利用ください。