ローカルや開発用のサーバーでWebアプリケーションをテストしていると、本番環境に近い動作確認のためにHTTPS通信が必須になる場面がよくあります。
最近、開発用PCに構築したNginxのWebサーバでhttps通信を使用する必要があったため、久しぶりにopensslを使って自己署名証明書(通称:オレオレ証明書)を作成してhttpsに対応させましたので、その際の作業内容をまとめたものを残しておきます。
秘密鍵と自己署名証明書の作成
SSL/TLS証明書の暗号化方式には、従来から広く使われているRSAと、よりモダンで高性能なECDSA(楕円曲線暗号)があります。
今回は、現在主流となりつつあるECDSA形式で証明書を作成しました。
ECDSAを使うメリット
RSAに比べて圧倒的に短い鍵長で同等以上のセキュリティを確保できるため、暗号化の計算処理が非常に高速です。
これにより、SSL/TLSハンドシェイク(通信開始時の暗号化の取り決め)が速くなり、Webサイトの表示速度が向上します。
これはモバイル端末など、処理能力が限られるデバイスで特に有利です。
ECDSAを使うデメリット
非常に古いブラウザやOS(Windows XP、Android 4.0以前など)ではサポートされていません。
証明書の作成コマンド
ECDSA方式の秘密鍵と、有効期間365日の自己署名証明書は、以下のコマンドで作成できます。
sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout 秘密鍵名 \ -addext "subjectAltName = DNS:Webサーバ名" \ -out 証明書名.crt
コマンドを実行すると、対話形式で証明書の情報を質問されるので適宜入力を行ってください。
自分で使用する開発用証明書の場合、最低限入力が必須の項目は「Common Name」(Webサイトのサーバ名)のみとなります。
「Common Name」には、-addextで指定したsubjectAltNameと同じドメイン名を入力するのが基本です。
その他の項目を入力しなかった場合は、[]で囲まれた項目の初期設定値が入力されます。
空欄にしたい場合は、「.」を入力してください。
Country Name (2 letter code) [AU]: 国コード (例: JP)
State or Province Name (full name) [Some-State]: 都道府県名 (例: Tokyo)
Locality Name (eg, city) []: 市区町村名 (例: Chiyoda)
Organization Name (eg, company) [Internet Widgits Pty Ltd]: 組織名 (例: server-memo.net Inc)
Organizational Unit Name (eg, section) []: 部署名 (例: development department)
Common Name (e.g. server FQDN or YOUR name) []: 必須項目 Webサーバ名 (例: conv.server-memo.net)
Email Address []: メールアドレス
自己署名証明書の作成例
「/etc/nginx/ssl_key」というディレクトリを作成して、そこに以下の内容で秘密鍵と自己署名証明書を作成しました。
- 秘密鍵: conv.server-memo.net.key
- 自己署名証明書: conv.server-memo.net.crt
- CommonName: conv.server-memo.net
- subjectAltName: conv.server-memo.net
証明書を保存するためのディレクトリを作成し、そこに移動します。
$ sudo mkdir /etc/nginx/ssl_key $ cd /etc/nginx/ssl_key
opensslコマンドを実行して、秘密鍵と自己署名証明書を生成します。
$ sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout conv.server-memo.net.key \ -addext "subjectAltName = DNS:conv.server-memo.net" \ -out conv.server-memo.net.crt
対話形式で証明書に含める情報を質問されるので、「Common Name」に「conv.server-memo.net」を入力、「Country Name」「State or Province Name」「Organization Name」には「.」(ピリオド)を入力して情報を空欄にしました。
----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]:. State or Province Name (full name) [Some-State]:. Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]:. Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:conv.server-memo.net Email Address []:
コマンドが完了すると、ディレクトリ内に秘密鍵「conv.server-memo.net.key」と、証明書「conv.server-memo.net.crt」の2つのファイルが作成されます。
- conv.server-memo.net.crt: 証明書は公開される情報なので、誰でも読み取れるパーミッションになっています。
- conv.server-memo.net.key: 秘密鍵は所有者(root)のみが読み書き出来るように、パーミッションが厳しく設定されています。
$ ls -l conv.server-memo.net.{key,crt} -rw-r--r-- 1 root root 652 8月 25 13:54 conv.server-memo.net.crt -rw------- 1 root root 241 8月 25 13:53 conv.server-memo.net.key
証明書の中身を確認する方法
作成した証明書が意図通りに生成されているか中身を確認してみます。
以下のコマンドを実行すると、作成された証明書の中身を確認できます。
openssl x509 -in 証明書 -text -noout
確認する際は以下の設定に間違いが無いかを確認しておきます。
- Validity(有効期間)
- Subject: CN
- X509v3 Subject Alternative Name
$ openssl x509 -in conv.server-memo.net.crt -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 4e:be:18:79:72:9c:bc:03:02:97:ba:8c:ea:bd:d2:9b:db:d0:91:e2 Signature Algorithm: ecdsa-with-SHA256 Issuer: CN = conv.server-memo.net Validity Not Before: Aug 25 04:54:02 2025 GMT Not After : Aug 25 04:54:02 2026 GMT Subject: CN = conv.server-memo.net Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:a0:67:7a:1b:31:b2:2c:e9:05:f7:eb:a5:92:ce: 5a:58:a8:99:04:3d:26:4d:81:99:9c:75:fa:d6:3d: b0:58:38:b3:d5:85:7e:1d:9f:36:e1:ca:89:50:4e: 96:37:bd:48:1a:af:0b:46:f1:3c:80:bf:53:ad:36: 95:52:d4:a2:ca ASN1 OID: prime256v1 NIST CURVE: P-256 X509v3 extensions: X509v3 Subject Key Identifier: 73:A5:7E:1C:D4:75:66:3F:7E:40:E6:AB:FB:A8:FF:1A:B7:27:18:1C X509v3 Authority Key Identifier: 73:A5:7E:1C:D4:75:66:3F:7E:40:E6:AB:FB:A8:FF:1A:B7:27:18:1C X509v3 Basic Constraints: critical CA:TRUE X509v3 Subject Alternative Name: DNS:conv.server-memo.net Signature Algorithm: ecdsa-with-SHA256 Signature Value: 30:44:02:20:35:8a:c8:9b:33:27:f3:e1:4d:39:15:86:e1:a6: f4:08:3c:a6:71:2e:c2:22:cd:81:9f:c1:05:aa:c8:54:7b:ea: 02:20:24:41:13:14:4e:49:19:30:70:42:71:e3:6e:52:27:e3: a0:27:8e:e8:48:89:01:28:75:e8:cd:e4:6e:41:94:24
NginxへのSSL/TLS設定
自己署名証明書の準備ができたので、次にNginxに設定を行い、HTTPS通信を有効化します。
作業内容は以下のとおりです。
- SSL共通設定ファイルを作成:複数のサイトで再利用できる、SSLの設定を別ファイルにまとめます。
- サイト別設定ファイル(バーチャルホスト)を修正:作成した共通設定を読み込み、証明書を指定します。
SSL共通設定ファイルを作成
SSL/TLSの設定は、暗号スイートの指定などで非常に長くなりがちです。
これらの設定をssl.confのような共通ファイルにまとめておくことで、各サイトの設定ファイルがスッキリし、メンテナンス性も格段に向上します。
設定を/etc/nginx/ssl.confファイルにまとめておけば、そのファイルを1つ修正するだけで、そのファイルを読み込んでいる全てのサイトに一括で設定を反映でき、メンテナンスが非常に楽になります。
$ cd /etc/nginx $ sudo vi ssl.conf
今回は、Let's Encryptの証明書を導入した際にcertbotが自動生成する、モダンで安全な設定をベースにします。
ssl_session_cache shared:le_nginx_SSL:10m; ssl_session_timeout 1440m; ssl_session_tickets off; ssl_protocols TLSv1.2 TLSv1.3; ssl_prefer_server_ciphers off; 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(HTTPS)を有効化します。
念のため、作業前に設定ファイルのバックアップを取っておくと安心です。
※html_converter.confは私が開発環境で使用している、バーチャルホストの設定ファイルなので、適宜読み替えてください。
$ cd conf.d $ sudo cp -p html_converter.conf html_converter.conf_$(date "+%Y%m%d_%H%M%S") $ sudo vi html_converter.conf
設定例は以下のとおりです。
server { listen 80; server_name conv.server-memo.net; # すべてのリクエストをhttpsの同一ホストへ301リダイレクト return 301 https://$host$request_uri; } server { listen 443 ssl; server_name conv.server-memo.net; index index.html; # 秘密鍵と証明書を指定 ssl_certificate /etc/nginx/ssl_key/conv.server-memo.net.crt; ssl_certificate_key /etc/nginx/ssl_key/conv.server-memo.net.key; # SSL共通設定ファイルの読み込み include /etc/nginx/ssl.conf; ##### 中略 ##### }
設定範囲
nginx -tで設定ファイルに文法エラーがないかを確認し、Nginxを再起動して設定を反映させます。
$ sudo nginx -t $ sudo systemctl restart nginx
これで、httpsでアクセスが可能になります。
動作確認と証明書の検証
Nginxの設定が完了したので、WebブラウザからHTTPSでアクセスし動作を確認します。
自己署名証明書を使っているため、ブラウザにはセキュリティ警告が表示されますが、これは想定通りの正常な動作です。
Chromeでアクセスすると、「この接続ではプライバシーが保護されません」といったセキュリティ警告が表示されます。
これは、証明書が公的な認証局ではなく「自分自身」によって署名されているため、ブラウザが「発行元を信頼できません」と警告しているためです。
開発環境ではこの警告は問題ないので、「詳細設定」をクリックします。
画面の下に表示される「Webサーバ名 にアクセスする(安全ではありません)」をクリックすると、Webサイトにアクセスすることが出来ます。
証明書の確認方法
ブラウザが受け取った証明書の作成したものであることを確認してみます。
URLバーに表示されている「保護されていない通信」部分をクリックします。
表示されたメニューにある「証明書の詳細」をクリックします。
証明書ビューアが表示されて、証明書の内容が表示されます。
(応用) 非対話形式での証明書作成
対話形式で情報を入力する代わりに、-subjオプションを使うと証明書の情報を事前に指定できるので、非対話形式で証明書を作成できます。
Common Nameのみを指定するシンプルな例
開発環境で最低限必要なCommon NameとsubjectAltNameだけを設定する、最もシンプルなコマンドです。
この場合、設定していない項目には初期設定の値が反映されます。
sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout 秘密鍵名 \ -subj "/CN=Webサーバ名" \ -addext "subjectAltName = DNS:Webサーバ名" \ -out 証明書名.crt
組織情報などをすべて指定する具体例
-subjで指定する文字列は「/」で始まり、各項目を「/」で区切って指定します。
sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout 秘密鍵名 \ -subj "/C=国コード/ST=都道府県名/L=市区町村名/O=組織名/OU=部署名/CN=Webサーバ名" \ -addext "subjectAltName = DNS:Webサーバ名" \ -out 証明書名
「.」を入力すると、その項目に空欄してすることが出来ます。
(応用) ワイルドカード証明書を作成する方法
1枚の証明書で、server-memo.netのようなベースドメインと、www.server-memo.netやconv.server-memo.netといった全てのサブドメインをまとめてカバーしたい場合があります。
このような証明書を「ワイルドカード証明書」と呼びます。
自己署名証明書でこれを実現するには、CommonNameとsubjectAltNameの設定が鍵となります。
- CommonName: *.ドメイン名
- subjectAltName: DNS:*.ドメイン名, DNS:ドメイン名
「DNS:*.ドメイン名」はすべてのサブドメインをカバーします。
ワイルドカード(*)はベースドメイン自体をカバーしないため「DNS:ドメイン名」の設定も必要になります。
作成コマンド例
非対話形式でワイルドカード証明書を作成するコマンドは以下のようになります。
sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout 秘密鍵名 \ -subj "/C=国コード/ST=都道府県名/L=市区町村名/O=組織名/OU=部署名/CN=*.ドメイン名" \ -addext "subjectAltName = DNS:*.ドメイン名, DNS:ドメイン名" \ -out 証明書名
作成例: *.server-memo.net をカバーする証明書
server-memo.netとその全てのサブドメイン(例: www.server-memo.net)を1枚でカバーする、ワイルドカード証明書を作成します。
-subjオプションでCommonNameに「*.server-memo.net」、-addextオプションでsubjectAltNameに「DNS:*.server-memo.net, DNS:server-memo.net」と設定した証明書を作成します。
$ sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout server-memo.net.key \ -subj "/C=JP/ST=Tokyo/L=Chiyoda/O=server-memo.net Inc/OU=IT Department/CN=*.server-memo.net" \ -addext "subjectAltName = DNS:*.server-memo.net, DNS:server-memo.net" \ -out server-memo.net.crt -----
このコマンドで生成したserver-memo.net.crtとserver-memo.net.keyをNginxに設定することで、ベースドメインであるserver-memo.netと、www.server-memo.netやmail.server-memo.netといった任意のサブドメインの両方で、この証明書を共通で利用できるようになります。
コマンドオプションの詳細解説
opensslコマンドで自己署名証明書を作成する際に使用する、各オプションの役割を解説します。
sudo openssl req -x509 -nodes -days 365 \ -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 \ -keyout 秘密鍵名 \ -subj "/CN=Webサーバ名" \ -addext "subjectAltName = DNS:Webサーバ名" \ -out 証明書名.crt
- req
- -x509
- -nodes
- -days 365
- -newkey ec
- -pkeyopt ec_paramgen_curve:prime256v1
- -keyout 秘密鍵
- -subj
- C: Country Name (国コード)
- ST: State or Province Name (都道府県名)
- L: Locality Name (市区町村名)
- O: Organization Name (組織名)
- OU: Organizational Unit Name (部署名)
- CN: Common Name (WebサーバのFQDN)
- -addext "subjectAltName = DNS:Webサーバ名"
- -out 証明書
証明書署名要求 (CSR) や証明書を扱うためのopensslサブコマンドです。
通常はCSRを作成するreqコマンドの動作を変更し、自己署名証明書を直接生成させます。
(No DESの略) 生成される秘密鍵をパスフレーズで暗号化しないようにするオプションです。
これを付けないと、Nginx起動時に毎回パスワードの入力が求められてしまうため、自動起動させたい場合は必須のオプションです。
証明書の有効期限を365日に設定します。
数字部分を変更することで、有効期限を自由に変更することが出来ます。
新しい秘密鍵を生成するためのオプションです。
ecを指定することで、鍵のアルゴリズムとして楕円曲線暗号 (Elliptic Curve) を使うよう指示します。
-newkeyに対する詳細な設定で、ec_paramgen_curve:prime256v1と指定することで、最も標準的で互換性の高いP-256という種類の楕円曲線を使用します。
作成する秘密鍵のファイル名を指定します。
非対話形式で証明書のサブジェクト情報を指定します。これにより、コマンド実行時に質問されるのを防ぎます。
-subjで指定する文字列は以下のように「/」で始まり、各項目を「/」で区切って指定します。
-subj "/C=国コード/ST=都道府県名/L=市区町村名/O=組織名/OU=部署名/CN=*.ドメイン名"
証明書の拡張領域にsubjectAltName (SAN)情報を追加します。
「subjectAltName」は「サブジェクトの別名」という意味で、現代のブラウザはCommon Nameではなく、このSANを見てドメイン名の一致を検証するため、事実上必須のオプションです。
「DNS:」はドメイン名であることを示す識別子です。
作成する証明書のファイル名を指定します。
コメント