備忘録 サーバー linux セキュリティ 暗号化

certbotを使用してCSRを使ったLet's Encryptの証明書を発行してみる

2017年5月4日

StartSSLが段階的に使えなくなってきてるため、Let's Encryptに乗り換えることにしました。

StartSSLが使えなくなることについてはこちらにて記載しています。

Let's Encryptとは

非営利団体 ISRG(Internet Security Research Group)が運営しており、TLSやHTTPSの普及を目的としたCA(Certificate Authority:認証局)である。
証明書を無料で発行することができる。

現在、MozzilaやGoogle・Cisco System・Akamai・EFF(電子フロンティア財団)などがスポンサーになっているようです。

特徴

  • 暗号方式:RSA(2048bit) ※ECDSAにも設定可能
  • 期間:3ヶ月(失効30日前より更新可)
  • マルチドメイン:可
  • EV証明書:発行不可
  • DNSレコード設定:必要(Aレコード)

証明書の有効期間が3ヶ月というとても短いです。これが嫌でStartSSLを使い続けていました。。。自動更新設定すればいい話なんですけどね。(公式にも自動更新推奨になってる。)

マルチドメインの設定は可能ですが、DNSにて名前解決ができていることが前提になります。(すべてAレコードでよい)
DNSに登録できないサブドメインの証明書を発行してもらうことはできないようです。(一時的にDNS登録したとしても3ヶ月ごとにやらないといけなくなりめんどくさい。)

当たり前ですが、EV証明書は発行できないようです。

事前準備

今回はCertbotを使用するため、yumにてインストールをします。

yum install certbot

証明書の作成

今回、Certbotというツールを用いて証明書を作成することにしました。
さらに、RSA(2048bit)ではなくECDSA P384(384bit)で暗号化し、マルチドメインも作成することにしました。
サーバはnginxです。
既にサーバ運用しているため、認証方法Webrootです。(443ポートが空いていればstandaloneでも使用できるみたいです。)

秘密鍵、証明書発行要求(CSR)の作成

マルチドメイン対応

まず、マルチドメイン対応を行うために/etc/pki/tls/openssl.cnfを編集します。


#~~~~~126行目付近~~~~~
[ req ]
default_bits = 2048
default_md = sha256
default_keyfile = privkey.pem
distinguished_name = req_distinguished_name
attributes = req_attributes
x509_extensions = v3_ca # The extentions to add to the self signed cert
# Passwords for private keys if not present they will be prompted for
# input_password = secret
# output_password = secret
# This sets a mask for permitted string types. There are several options.
# default: PrintableString, T61String, BMPString.
# pkix : PrintableString, BMPString (PKIX recommendation before 2004)
# utf8only: only UTF8Strings (PKIX recommendation after 2004).
# nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings).
# MASK:XXXX a literal mask value.
# WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings.
string_mask = utf8only

req_extensions = v3_req # The extensions to add to a certificate request
####↑↑req_extensions = v3_caをコメント解除↑↑####

#~~~~~225行目付近~~~~~
[ v3_req ]
<h1>Extensions to add to a certificate request</h1>
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names
####↑↑subjectAltName = @alt_names を追記↑↑####

####↓↓alt_names部分全て追記↓↓####
[alt_names]
DNS.1 = kaede.jp
DNS.2 = aaa.kaede.jp
DNS.3 = bbb.kaede.jp
DNS.4 = ccc.bbb.kaede.jp
####DNS.〇の順にマルチドメインを追記する。〇は数値
####※全ドメインがDNS(Aレコード)に登録されており、Let's Encrypt承認サーバから各ドメインにアクセスができなければ証明書発行ができないので注意すること
####IP.〇については未検証

ECDSAによる証明書作成

設定が完了すれば、ECDSAによる証明書作成を行います。

# cd 【証明書を保存したい場所】
# openssl req -new -nodes -newkey ec:<(openssl ecparam -name secp384r1) -subj "/C=/ST=/L=/O=/OU=/CN=kaeede.jp" -keyout server.key -out server.csr
Generating a 384 bit EC private key
## writing new private key to 'server.key'
No value provided for Subject Attribute C, skipped
No value provided for Subject Attribute ST, skipped
No value provided for Subject Attribute L, skipped
No value provided for Subject Attribute O, skipped
No value provided for Subject Attribute OU, skipped

# ll
-rw-r--r-- 1 root root 615 4月 30 02:06 server.csr
-rw-r--r-- 1 root root 306 4月 30 02:06 server.key

openssl req -new -nodes -newkey ec:<(openssl ecparam -name 【ESCDSAの種類】) -subj "/C=/ST=/L=/O=/OU=/CN=【メインドメイン】" -keyout 【秘密鍵ファイル名】.key -out 【証明書発行要求ファイル名】.csr
にて作成できます。ECDSAの種類は「openssl ecparam -list_curves」で確認することができます。

  • secp384r1 : NIST/SECG curve over a 384 bit prime field #P-384曲線
  • secp521r1 : NIST/SECG curve over a 521 bit prime field #P-521曲線
  • prime256v1: X9.62/SECG curve over a 256 bit prime field #P-256曲線

曲線の種類はOpenSSLによって異なり、上記以外の曲線は証明書として使えないこともあるので注意してください。(P-521曲線は使えないものあるのでP-256曲線かP-384曲線がオススメ)

新しい暗号アルゴリズムを使う=古いOSやブラウザは対象外(使えない!)となる場合があります。
どのOSもブラウザも使用したい…ならばRSAで証明書発行することが無難です。(RSA2048bitは2030年まで大丈夫とされていますしね。)

4.x以下の古いAndroidを使用しているユーザがいる場合は注意です。キーストアにECDSAが使えるようになったのはAndroid4.4からです。(Security Enhancements in Android 4.4
スマタブinfoにてシェア率を見てみるとAndroid4.xでも15.4%いますしね。(Android4.3以下だと5.43%ですが)

Nginxの設定

今回はWebrootによる認証を行いますので、Nginxに「.well-known」ディレクトリのアクセス先を設定します。

server {
	listen 80 default_server;
	server_name kaede.jp;
	charset UTF-8;
	・
	・
	・
	#Let's Encryptディレクトリ
	location ^~ /.well-known {
		root 【認証ファイルを配置したいフォルダ(例:/aaa/LetsEncrypt)】;
	}
}

80番ポートの設定のみでOK。マルチドメインの設定情報がある場合は同様に追記していきます。
通常、ドメイン毎に「.well-known」の指定及び作成しなくていいように別ディレクトリで作成するようにしています。

「/aaa/LetsEncrypt」に「.well-known」フォルダを作成⇒認証の順になります。
上記方法は「.」から始まるファイル名(.htaccessとか)およびディレクトリ(.svnとか)のアクセス拒否を行っている場合でも使えます。

HSTS(HTTP Strict Transport Security) 設定をしている場合でも毎回httpで存在確認してくれるようですので80ポートのみ開放設定でOKです。

Certbotによる証明書発行申請

以下のコマンドで発行できます。


certbot certonly --webroot --csr server.csr -m example@kaede.jp --renew-by-default --agree-tos -w /aaa/LetsEncrypt -d kaede.jp
No handlers could be found for logger "certbot.crypto_util"
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Starting new HTTPS connection (1): acme-v01.api.letsencrypt.org
Performing the following challenges:
http-01 challenge for kaede.jp
http-01 challenge for aaa.kaede.jp
http-01 challenge for bbb.kaede.jp
http-01 challenge for ccc.bbb.kaede.jp
Using the webroot path /aaa/LetsEncrypt for all unmatched domains.
Waiting for verification...
Cleaning up challenges
Server issued certificate; certificate written to /xxx/certs/0000_cert.pem
Cert chain written to <fdopen>
Cert chain written to <fdopen>

IMPORTANT NOTES:
- Congratulations! Your certificate and chain have been saved at
/xxx/certs/0001_chain.pem. Your cert will expire on
2017-07-28. To obtain a new or tweaked version of this certificate
in the future, simply run certbot again. To non-interactively renew
<em>all</em> of your certificates, run "certbot renew"
- If you like Certbot, please consider supporting our work by:

Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
Donating to EFF: https://eff.org/donate-le
<h1>ll</h1>
合計 20
-rw-r--r-- 1 root root 1610 4月 30 02:18 0000_cert.pem
-rw-r--r-- 1 root root 1647 4月 30 02:18 0000_chain.pem
-rw-r--r-- 1 root root 3257 4月 30 02:18 0001_chain.pem
-rw-r--r-- 1 root root 615 4月 30 02:06 server.csr
-rw-r--r-- 1 root root 306 4月 30 02:06 server.key

CSR作成時にてマルチドメインを設定している場合は「-w」及び「-d」の設定は1つで問題ないです。
マルチドメインを設定していない場合は作成したいドメイン分「-d」と「-w」の設定を行います。

問題なければ

  • 0000_cert.pem(サーバ証明書)
  • 0000_chain.pem(中間CA証明書
  • 0001_chain.pem(中間CA証明書+サーバ証明書)

の3ファイルが作成されます。

オプション

certbotのオプションは下記の通りです。

  • certonly:証明書発行のみ
  • --webroot:Webrootによる認証
  • --csr 【CSRファイルパス】:CSRファイルの指定
  • -m 【メールアドレス】:登録するメールアドレスの指定
  • --renew-by-default:証明書の更新設定
  • --agree-tos:利用規約の同意
  • -w 【ディレクトリ】:webroot(.well-knownを作成する)ディレクトリ
  • -d 【ドメイン】 証明書を発行したいドメイン
  • --dry-run:テストモード
  • --expand:更新モード(?)
  • --cert-path 【ファイル名】:CRT(証明書)ファイル名
  • --chain-path 【ファイル名】:CA証明書のファイル名

Nginxへサーバ証明書の設定

OSやブラウザによってLet's Encryptの中間CA証明書を持っていない場合がある為、「0001_chain.pem」をnginxに設定します。

server {
	listen 443 ssl http2 default_server;
	listen [::]:443 ssl http2 ipv6only=on;
	server_name kaede.jp;
	・
	・
	・
	ssl_certificate /xxx/certs/0001_chain.pem;
	ssl_certificate_key /xxx/certs/server.key;
}

設定後、nginxをリロードし、証明書が更新されていればOKです。

課題点

CSRを用いてサーバ証明書がうまくいくのかどうかは未確認です。

certbot renewを使えば--pre-hookや--post-hookを使うことができるのでサーバ再起動は容易にできるのですが、certbot certonlyには使えないっぽい。
certbot renewを使えば-csrが使えない…。
certbot certonlyで証明書更新しようとするとファイルの書込みができないと怒られる…。

証明書の有効期限が7月29日まであるので、ゆっくり模索してみることにします。

-備忘録, サーバー, linux, セキュリティ, 暗号化
-, , , , ,