プライベート認証局によるサーバー証明書の発行

はじめに

プライベート認証局 (オレオレ認証局) を構築してサーバー証明書を発行し、WebサーバーをHTTPS化しました。本校ではそのうちプライベート認証局の構築からサーバー証明書の発行と、ルート証明書のWebブラウザへの登録までを紹介します。

なぜプライベート認証局を構築するのか

筆者はローカル環境にApacheなどのWebサーバーを立てて、開発やテストに利用しています。HTTPSが必要な場合は、自己署名証明書 (いわゆるオレオレ証明書) を使ってWebサーバーのHTTPS化を行なってきました。自己署名証明書をWebブラウザから読み込んで「例外」として承認することで、HTTPSでのアクセスを可能にする方法です。

ただこの方法では、複数のWebサーバーがある場合は、それぞれの証明書を読み込む必要があります。また例外として承認した後も、Webブラウザによっては「保護されない通信」という警告がアドレスバーに表示され続けます。

自己署名証明書を作ってルート証明書として読み込んでみた
自己署名証明書 (オレオレ証明書) をルート証明書としてインポートしたときのブラウザの挙動をテストしてみました。ブラウザと証明書の作成方法によって挙動が違うことが判ったので、その結果を報告します。

これに対してプライベート認証局を構築した場合は、認証局のルート証明書をWebブラウザに一度だけ読み込めば、サーバーごとの例外処理は必要ありません。また「保護されない通信」という警告も表示されません。

本稿は、開発やテスト目的の利用を想定対象としています。本番環境での利用には、証明書の配布や失効など、それなりの運用基盤が必要になります。本稿はそこまではカバーしておらず、手作業での証明書の配布や削除を前提としています。

参考文献

本稿の内容は下記のオンラインブックの解説に従っています。

OPENSSL COOKBOOK 3rd Edition
The Definitive Guide to the Most Useful Command Line Features
by Ivan Ristic
https://www.feistyduck.com/books/openssl-cookbook/

スポンサーリンク

HTTPSと認証局の役割

HTTPS通信における認証局の役割については次の記事を参考にしてください。

HTTPSにおける認証局の役割を理解する
HTTPS通信における認証局の役割について説明しています。HTTPS通信における暗号化を実現する鍵の役割と、鍵を安全にやり取りする仕組み、その仕組みを支える認証局とサーバー証明書について解説しています。

プライベート認証局の構築 (@認証局)

「局」という言葉から、常時稼働するサーバーを想像しますが、そうではありません。証明書の組み込みはWebサーバーの構築時に行われるため、実際に通信が行われる際には、認証局が稼働している必要はありません。むしろセキュリティの観点からは、認証局は普段はネットワークから切り離しておくべき、というガイドもあります。筆者のような個人環境であれば、手元のパソコンに証明書を発行するプログラムが入っていれば十分です。

筆者の場合は、UbuntuサーバーをVM上に立ち上げて認証局としています。VMを使っているのは、手元のパソコン環境をできるだけシンプルにしたいだけで、特に必要性はありません。

認証局の構築にはopenssl version 3を使用します。次のコマンドを叩いて、インストールの有無とバージョンを確認します。

openssl version

筆者の環境では次のように出力されました。

OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)

参考文献では、ルート認証局と中間認証局を構築していますが、ここでは簡略化して、ルート認証局のみを構築するものとします。

環境構築

ホームディレクトリ下に作業ディレクトリを作成します。

cd ~
mkdir root-ca
cd root-ca
mkdir certs db private
chmod 700 private
touch db/index
openssl rand -hex 16 > db/serial

認証局の証明書の作成

認証局の構築に必要な設定を定義した構成ファイルを用意します。

vim root-ca.conf

構成ファイルに次の内容をコピーしてください。

name_opt = utf8,esc_ctrl,multiline,lname,align

[req]
default_bits = 4096
encrypt_key = no
default_md = sha256
utf8 = yes
string_mask = utf8only
prompt = no
distinguished_name = req_dn
req_extensions = req_ext

[req_dn]
countryName = "JP"
organizationName ="Hoge CA"
commonName = "Root CA"

[req_ext]
basicConstraints = critical,CA:true
keyUsage = critical,keyCertSign
subjectKeyIdentifier = hash

[ca]
default_ca = CA_default

[CA_default]
name = root-ca
home = .
database = $home/db/index
serial = $home/db/serial
certificate = $home/$name.crt
private_key = $home/private/$name.key
RANDFILE = $home/private/random
new_certs_dir = $home/certs
unique_subject = no
copy_extensions = none
default_days = 3650
default_md = sha256
policy = policy_match

[policy_match]
countryName = supplied
stateOrProvinceName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

この構成ファイルには、幾つかのセクションが定義されています。この後の作業によって、関係するセクションが参照されます。

[req] – 認証局の証明書への署名要求の作成で参照されます。

[req_dn], [req_ext]は、[req]の中で参照されています。これらは複数の定義を切り替える場合に備えて、サブセクションとして定義されています。

[ca] – 認証局の証明書に自己署名する時に参照されます。

[CA_default]は[ca]から、 [policy_match] は、[CA_default]から参照されています。これらも複数の定義を切り替える場合に備えて、サブセクションとして定義されています。

下記の項目については、それぞれの環境に合わせて変更してください。認証局の自身の情報となります。プライベートなので、organizationNameやcommonNameは自由につけて良いです。

[req_dn]
countryName = "JP"
organizationName = "Hoge CA"
commonName = "Root CA"

認証局自身の証明書を作成します。

まず秘密鍵 (root-ca.key) と署名要求 (root-ca.csr) を作成します。

cd ~/root-ca
openssl req -new -config root-ca.conf -out root-ca.csr -keyout private/root-ca.key

この認証局はルート認証局の扱いとなるため、自己署名により証明書 (root-ca.crt) を作成します。

openssl ca -selfsign -config root-ca.conf -in root-ca.csr -out root-ca.crt -extensions req_ext

署名用構成ファイルの作成

認証局として、今後サーバー証明書に署名するために、必要な設定を定義した構成ファイルを用意します。

cd ~/root-ca
vim sign-server.conf

構成ファイルに次の内容をコピーしてください。

name_opt = utf8,esc_ctrl,multiline,lname,align

[ca]
default_ca = CA_default

[CA_default]
name = root-ca
home = .
database = $home/db/index
serial = $home/db/serial
certificate = $home/$name.crt
private_key = $home/private/$name.key
RANDFILE = $home/private/random
new_certs_dir = $home/certs
unique_subject = no
copy_extensions = copy
default_days = 365
default_md = sha256
policy = policy_match

[policy_match]
countryName = supplied
stateOrProvinceName = optional
organizationName = supplied
organizationalUnitName = optional
commonName = supplied
emailAddress = optional

[server_ext]
authorityKeyIdentifier = keyid:always
basicConstraints = critical,CA:false
extendedKeyUsage = serverAuth
keyUsage = critical,digitalSignature,keyEncipherment
subjectKeyIdentifier = hash

先に作成した自己署名用の構成とは下記の点が異なっています。

  • [req]に関する項目がない
    ここでは署名のみ行うので署名要求の作成に関する項目は必要ありません。
  • [CA-default]
    • copy_extensions = copy
      署名要求の追加項目 (subjectAltName) を証明書にコピーします。
    • default_days = 365
      有効期間を1年に変更しています。
  • [server_ext]を追加
    サーバー証明書に必要な幾つかの設定が定義されています。

ルート証明書の受け取り (@クライアントPC)

クライアントPCで、認証局からのルート証明書を受け取り、Webブラウザが参照できるようにします。

まず認証局から証明書 (root-ca.crt) を受け取ります。<user>, <ca-server>は適宜置き換えてください。

scp <user>@<ca-server>:~/root-ca/root-ca.crt root-ca.crt

ルート証明書のWebブラウザへの読み込み (@クライアントPC)

Webブラウザへのインポートの方法は、ブラウザの種類と使用OSによって異なります。

Mac OSの場合

筆者の手元にあるMac OS (Ventura 13.3) の環境では次の通りです。

Safari / Chrome / Edge

Safari、ChromeとEdgeは、OSのキーチェーンに格納された証明書を参照しています。このためキーチェーンアクセスを起動して、ルート証明書を読み込みます。

デフォルトキーチェーン > ログイン > 証明書 > 新規のキーチェーン項目を作成します。

筆者の環境 (Ventura 13.2.1) では、当初は新規のキーチェーン項目を作成します。のアイコンが、無効化されたままでした。そこでメニューで、iCloud > ログイン と切り替えると、アイコンが有効になりました。操作によって再現したりしなかったりなので、筆者の環境固有の問題かも知れませんが、もし同じ問題に当たったら、上記を試してみてください。

アイコンをクリックすると、ファイル選択のダイアローグが表示されるので、先ほど取得したroot-ca.crtファイルを開きます。

証明書リストに、Root CAが追加されます。この段階ではリスト上のアイコンに❌がついていて、説明欄には「ルート認証局」の表示はあるものの、「このルート証明書は信頼されていません」と表示されています。

ダブルクリックして証明書を開きます。> 信頼 というセクションが表示されているので開きます。

この証明書を使って信頼すべき対象の一覧が表示されます。ここでSSLのプルダウンから常に信頼を選択します。

ダイアローグを閉じると、変更に対するパスワードを要求されるので入力します。

リスト表示に戻ると先ほどの❌が消えています。また説明欄には「この証明書はこのアカウントにとって信頼されているものとして指定されています」と表示されています。

Webブラウザから証明書を読み込むこともできます。この場合もキーチェーンアクセスが起動されます。

Webブラウザのメニュー > 設定 > プライバシーとセキュリティ > 証明書 > デバイス証明書の管理

Firefox

FirefoxはOSのキーチェーンではなく、独自の証明書ストアを持っています。そのストアに認証局の証明書を読み込みます。

Webブラウザのメニュー > 設定 > プライバシーとセキュリティ > 証明書 > 証明書を表示

証明書マネージャーのダイアローグが開きます。

証明書マネージャー > 認証局証明書 > 読み込む

ファイル選択のダイアローグが開くので、先ほど取得したroot-ca.crtを開きます。

「”Root CA”が行う認証のうち、信頼するものを選択してください。」と表示されるので、「この認証局によるウェブサイトの識別を信頼する」をチェックします。

OKすると証明書にRoot CAの証明書が追加されます。(画面例のHoge CAは、認証局の自己署名の時に指定した組織名)

Windowsの場合

Chrome / Edge

ChromeとEdgeは、OSの証明書ストアに格納された証明書を参照しています。このためWindowsの管理コンソールから、証明書を読み込みます。

タスクバーで証明書の管理コンソールを検索します。

certmgr.msc

検索結果に次のように表示されたら、「開く」をクリックして起動します。

管理コンソールが起動したら証明書をインポートします。

信頼されたルート証明機関 > 証明書 > すべてのタスク > インポート

証明書のインポートウィザードが起動します。途中選択を求められますが、次の項目を選択します。

保存場所: 現在のユーザー

証明書ストア: 証明書をすべて次のストアに配置する > 信頼されたルート証明機関

最後にセキュリティ警告で、証明書の発行者が確認できない旨のメッセージが出ます。これは自己署名証明書であるためです。自分で作成した証明書なので「はい」をクリックして、証明書を受け入れます。

Firefox

FirefoxはOSの証明書ストアではなく、独自の証明書ストアを持っています。そのストアに認証局の証明書を読み込みます。

Webブラウザのメニュー > 設定 > プライバシーとセキュリティ > 証明書 > 証明書を表示

証明書マネージャーのダイアローグが開きます。

証明書マネージャー > 認証局証明書 > 読み込む

ファイル選択のダイアローグが開くので、先ほど取得したroot-ca.crtを開きます。

「”Root CA”が行う認証のうち、信頼するものを選択してください。」と表示されるので、「この認証局によるウェブサイトの識別を信頼する」をチェックします。

OKすると証明書にRoot CAの証明書が追加されます。(画面例のHoge CAは、認証局の自己署名の時に指定した組織名)

サーバー証明書の作成 (@認証局)

署名要求の作成

本来署名要求は、Webサーバー側で作成して、認証局に送付する形が一般的と思われます。ここでは認証局と同じサーバー上で、Webサーバーの秘密鍵や署名要求を作成することで、簡略化しています。

Webサーバーごとの証明書を作成するので、その格納ディレクトリを作成します。

cd ~
mkdir web-server
chmod 700 web-server

複数のサーバーがある場合は、”web-server” を対象のサーバー名などで適宜置き換えて、それぞれのサーバー用にディレクトリを用意すると良いでしょう。

まず秘密鍵 (web-server.key) を作成します。

cd ~/web-server
openssl genpkey -out web-server.key -algorithm RSA -pkeyopt rsa_keygen_bits:2048

次に署名要求 (web-server.csr) を作成します。署名要求には、subjectAltName を含める必要があります。Webブラウザによっては、Common Nameではなく、subjectAltName を参照するものがあるからです。example.com は、証明書発行対象のサーバーのFQDN (Fully Qualified Domain Name)で適宜置き換えてください。

openssl req -new -key web-server.key -out web-server.csr -addext "subjectAltName=DNS:example.com"

DNS名ではなくIPアドレスを指定する場合は、次のように指定します。

subjectAltName=IP:aaa.bbb.ccc.ddd

下記の項目について質問されるので、適宜指定してください。

Country Name (2 letter code) [AU]: JP
State or Province Name (full name) [Some-State]: Tokyo
Locality Name (e.g. city) []: Chiyoda
Organization Name (eg. company) [Internet Widgits Pty Ltd]: Hoge LLC
Organiztion Unit Name (eg. section) []: Home
Common Name (e.g. server FQDN or YOUR name) []: example.com
Email Address [];

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

必須項目は、Country Name, Organization Name, Common Nameです。

Common Nameは、Webブラウザに入力されたアドレスと比較されます。WebサーバーがFQDNを持っている場合は、それを指定します。IPアドレスしか持たない場合は、IPアドレスを指定します。

A challenge passwordは設定しないでください。設定した場合は、Webサーバーの起動時に、パスワードの入力が必要になります。

プライベート認証局による署名

Webサーバーの署名要求 (web-server.csr) にプライベート認証局の秘密鍵で署名して、署名済み証明書 (web-server.crt) を作成します。

cd ~/root-ca
openssl ca -config sign-server.conf \
-in ../web-server/web-server.csr \
-out ../web-server/web-server.crt \
-extensions server_ext

署名済みの証明書が、~/web-server/web-server.crt として作成されます。

Webサーバーへの証明書の組み込み

ここで作成したサーバー証明書を組み込んで、WebサーバーをHTTPS化する例について紹介しています。

Apacheをプライベート認証局やオレオレ証明書でHTTPS化
Apacheで構築したWebサーバーのHTTPS化について紹介します。サーバー証明書には、プライベート認証局で署名した証明書、または自己署名した証明書を使っています。
Nginxをプライベート認証局やオレオレ証明書でHTTPS化
Nginxで構築したWebサーバーのHTTPS化について紹介します。サーバー証明書には、プライベート認証局で署名した証明書、または自己署名した証明書を使っています。

補足: 認証の失効

認証局は証明書に署名するだけでなく、署名した証明書に対する失効 (取り消し) の処理も行います。例えば、Webサーバーの秘密鍵が漏洩した場合は、そのサーバーの証明書を失効させる必要があります。

失効の処理については、CRL (Certificate Revocation List – 失効リスト)の配布、OCSP (Online Certificate Status Protocol) での応答、といった仕組みを認証局で用意することで対応できるようです。WebブラウザやWebサーバーは、これらの仕組みを利用して、証明書が失効していないかどうか調べることができるとなっています。

ただしChromeの場合はこれらを使わないとなっていて、プライベート認証局で失効処理が実現可能かどうか不明です (筆者の勉強不足です …)。

冒頭にも書いたように本稿では失効の処理については範囲外としています。

更新記録

日付内容
2023/07/11HTTPSと認証局の役割を別記事に分割
2023/05/03ApacheのHTTPS化を別記事に分割しタイトルを変更
2023/05/02初版リリース