前回 Root CA, Issuing CA を作ったので、今回はサーバ証明書を作ってみる。クライアント証明書は次回。
Webサーバ用のSSL証明書を作り、nginx でTLSを有効にする。中間CA (Issuing CA) が挟まっていても動くようにする。
Web上には、認証局 (CA) などをすっ飛ばして, 単にオレオレ証明書を作ってそれでよし、としている解説ページが多い。現実と gap がある。
また、検索で上位にくるところでも, 用語の使い方が混乱していることがある。場合によっては、はっきり誤った使い方をしているサイトもあったりする。
環境: Fedora 22 Linux + nginx 1.8.0
ちゃんとしたサイトがどうなっているか確認する。opensslコマンドで, どの証明書が使われているか、などが見える。
$ openssl s_client -connect www.google.co.jp:443 CONNECTED(00000003) --- Certificate chain 0 s:/C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com Webサーバ証明書 i:/C=US/O=Google Inc/CN=Google Internet Authority G2 1 s:/C=US/O=Google Inc/CN=Google Internet Authority G2 i:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA 2 s:/C=US/O=GeoTrust Inc./CN=GeoTrust Global CA i:/C=US/O=Equifax/OU=Equifax Secure Certificate Authority これがRoot CA --- Server certificate -----BEGIN CERTIFICATE----- 証明書(略) -----END CERTIFICATE----- subject=/C=US/ST=California/L=Mountain View/O=Google Inc/CN=google.com issuer=/C=US/O=Google Inc/CN=Google Internet Authority G2 --- No client certificate CA names sent Server Temp Key: ECDH, prime256v1, 256 bits --- SSL handshake has read 10723 bytes and written 331 bytes --- New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES128-GCM-SHA256 Server public key is 2048 bit Secure Renegotiation IS supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1.2 Cipher : ECDHE-RSA-AES128-GCM-SHA256 Session-ID: 117B3D1810B9D8B4FD08509DA970EFB11460B870C68F7786DF94824BC2A3E4DA Session-ID-ctx: Master-Key: FF18B2DAF61D699C372DE83478AC2DB66EF975A0AFAE721EF99AAF652AA9F3ADF6ED6AEC9C5EC276F8D2F9AC6E56E53E Key-Arg : None Krb5 Principal: None PSK identity: None PSK identity hint: None TLS session ticket lifetime hint: 100800 (seconds) TLS session ticket: 0000 - 9e 以下、略
Webブラウザなどの証明書ストアでサーバ証明書を表示し、いくつかの属性がどうなっているか。
Critical Is not a Certificate Authority
Not Critical Signing
別のサイトのSSL証明書で、次のようになっているものもあった;
Critical Signing Key Agreement
Not Critical TLS Web Server Authentication (1.3.6.1.5.5.7.3.1) TLS Web Client Authentication (1.3.6.1.5.5.7.3.2)
まずは, 認証局 (CA) に対する署名要求 (CSR) を作る。
openssl のための設定ファイルを作る。こんな感じ。自分自身では署名しないので,「x509_extensions = cert_type
」はコメントアウト。
server-req.cnf
[ req ] default_bits = 2048 encrypt_key = yes distinguished_name = req_dn #x509_extensions = cert_type prompt = no # これを追加 req_extensions = v3_req [ req_dn ] # country (2 letter code) C=JP # State or Province Name (full name) ST=FUKUOKA # Locality Name (eg. city) #L=Helsinki # Organization (eg. company) O=Netsphere Laboratories # Organizational Unit Name (eg. section) #OU=IMAP server # Common Name (*.example.com is also possible) # "CN=*.fruits" だけではダメ。証明書は作れるが, Webブラウザが「不正なセキュリ # ティ証明書」などのエラーを出して、Webサーバに接続できない CN=*.kiwi.fruits # E-mail contact #emailAddress=hisashi.horikawa@gmail.com [ v3_req ] subjectAltName = @alt_names [ alt_names ] DNS.1 = *.kiwi.fruits DNS.2 = kiwi.fruits
Webサーバの SSL秘密鍵と署名要求 (CSR) を作る.
(2018.3) /etc/pki/tls/misc/CA
ファイルは、もはやない。openssl-perl パッケージの /usr/bin/CA.pl
になった。環境変数名も変更。
server$SSLEAY_CONFIG="-config server-req.cnf" /etc/pki/tls/misc/CA -newreqOPENSSL_CONFIG="-config server-req.cnf" /usr/bin/CA.pl -newreq Generating a 2048 bit RSA private key ......................................................................+++ ..............................+++ writing new private key to 'newkey.pem' Enter PEM pass phrase: Webサーバの秘密鍵のパスフレーズ Verifying - Enter PEM pass phrase: ----- Request is in newreq.pem, private key is in newkey.pem
秘密鍵ファイル newkey.pem
と署名要求ファイル newreq.pem
ができる。
次のコマンドでCSRを確認すると、Requested Extensions
セクション内に X509v3 Subject Alternative Name
というフィールドができていて、DNS 制約がある。
$ openssl req -text < newreq.pem
続けて、署名要求ファイルを中間CAのディレクトリにコピーしたうえで、中間CAで署名する。
設定ファイル /etc/pki/tls/openssl.cnf
をコピーしてきて、修正する。
Requested Extensions
で要求された X509v3 Subject Alternative Name をそのまま受け入れて署名する方法があるはずだが、分からなかった。ここでは、設定ファイルで同じ内容を書いている。
[ CA_default ] # ここを変更 dir = /home/hori/my-ca/intermediate-ca # Where everything is kept
# このセクションを追加 [ alt_names ] DNS.1 = *.kiwi2.fruits DNS.2 = kiwi2.fruits [ usr_cert ] # These extensions are added when 'ca' signs a request. # ここを変更 basicConstraints=critical, CA:FALSE # ここを変更. SSL サーバ # deprecated: Netscape Certificate Type # client, server, email, objsign, sslCA, emailCA, objCA nsCertType = server # ここを変更keyUsage = critical, digitalSignature, keyAgreementkeyUsage = critical, digitalSignature # これを追加 extendedKeyUsage = serverAuth, clientAuth # これを追加. @xxx はセクション名 # TODO: リクエストどおりに追加する方法が分からない subjectAltName=@alt_names
RFC 5280 によれば, digitalSignature
だけでよい。www.google.com
ほかいくつかのサイトはそのようになっている。keyEncipherment
も付けているサイトも多いが。
今回は手を抜いて, /etc/pki/CA
以下にディレクトリレイアウトを合わせる。例えば, private/
に中間CAの秘密鍵を置いたりする。
CAスクリプトは, -signCA ではなく -sign のほうを呼び出す。
intermediage-ca$SSLEAY_CONFIG="-config cert-ssl-req.cnf" /etc/pki/tls/misc/CA -signOPENSSL_CONFIG="-config sign-tls-server-req.cnf" /usr/bin/CA.pl -sign Using configuration from cert-ssl-req.cnf Enter pass phrase for /home/hori/my-ca/intermediate-ca/private/cakey.pem: 中間CAの秘密鍵のパスワード Check that the request matches the signature Signature ok Certificate Details: Webサーバ証明書の内容が表示される Certificate is to be certified until Aug 2 05:17:08 2016 GMT (365 days) Sign the certificate? [y/n]:y
Webサーバの証明書 newcert.pem
ファイルが生成される。
きちんと中間CAで署名できたか、確認する。Subject, Issuer, X509v3 extensions 辺りに注意。X509v3 Subject Alternative Name もあるか。
$ openssl x509 -text < newcert.pem
ここまでできたら、次は nginx への組み込み。
Webサーバ SSLの秘密鍵と証明書、それから中間CA (Issuing CA) の証明書を用意します。
Webサーバ証明書と中間CA証明書を一つのファイルにします。
$ cat ssl-server.cert.pem intermediate-ca.cert.pem > cert-chain.cert
また, Webサーバ秘密鍵PEMファイルからパスワードを外します。
$ openssl rsa -in ssl-server.encrypted_private.pem -out ssl-server.private.pem
Enter pass phrase for ssl-server.encrypted_private.pem
: サーバ秘密鍵のパスワード
writing RSA key
パスワードで保護された秘密鍵は「-----BEGIN ENCRYPTED PRIVATE KEY-----
」, 保護されていないほうは「-----BEGIN RSA PRIVATE KEY-----
」で始まります。
(2020.5更新)
Fedora 32 には nginx v1.18.0 パッケージがある。こちらでは, 設定ファイルは /etc/nginx/conf.d/*.conf
に置く。以下, /etc/nginx/tls
以下に証明書ファイルを置く場合の設定。
TLS 1.0, TLS 1.1 は無効にすること。2020年9月8日に, Internet Explorer 11 でも TLS 1.1 が無効になる。nginx の設定では複数の server
block を書くことが通常だが, どれか一つででもこれらが有効になっていると、すべての server
について有効になってしまう。http
block に書くのがよい。
http { # SSLv3 must be eliminated. # 2020.9.8 All browsers remove TLSv1.1 support. ssl_protocols TLSv1.2 TLSv1.3; # 暗号スイートを限定 ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; server { listen 443 ssl http2; listen [::]:443 ssl http2; server_name www.kiwi.fruits; # サーバ証明書 + 中間CA証明書 ssl_certificate tls/cert-chain.cert; # サーバ秘密鍵 ssl_certificate_key tls/private/ssl-server.private.pem; location / { 略... } }
参考: Qualys SSL LabsでスコアAがとれるnginxのTLS/SSLの設定方法 - kumaaaaaaa! やや内容が古いが、考え方は変わらず。
単に https://...
で接続すると次のようになる;
SEC_ERROR_UNKNOWN_ISSUER は、発行者が信頼できないエラー。サーバの証明書から辿っていって、ルート認証局の証明書が見つからないので表示される。
クライアントのWebブラウザで証明書ストアを開き、オレオレRoot CAの証明書をインポートする。中間CAの証明書や、Root CAの秘密鍵は不要。
(2024.02) Chrome v121 なら「証明書の管理」→「認証局」タブ→ [インポート] ボタン。"ウェブサイトの識別でこの証明書を信頼します" にチェックを入れる。
証明書ストアに認証局として登録されたことを確認する。
再度、Webサーバにアクセス。今度は成功。