
はじめに
こんにちは、ヤマウチです。
担当しているサービスではサーバ認証に加えてクライアントの認証も行う相互認証(mTLS)も使えるようになっています。相互認証を使う場合、Webサーバにサーバ証明書、クライアントにクライアント証明書を設定することになりますが、証明書の有効期間が切れる前に証明書の更新を行う必要があります。
この記事では、クライアント証明書の更新作業を具体的な設定例を交えて説明します。また、相互認証のうちクライアントの認証を行う部分の処理をクライアント認証と呼ぶことにします。
目次
サーバ認証とクライアント認証の比較
サーバ認証とクライアント認証を比較することでクライアント証明書の更新で気をつけるべきポイントが分かりやすくなると思いますので、両者を比較した表を示します。
表1. サーバ認証とクライアント認証の比較
| 項目 | サーバ認証 | クライアント認証 |
|---|---|---|
| 認証される対象 | サーバ | クライアント |
| 証明書を提示する側 | サーバ | クライアント |
| 証明書を検証する側 | クライアント | サーバ |
| 証明書・秘密鍵の設定場所 | サーバ | クライアント |
| 証明書の更新作業 | サーバ | クライアントとサーバ |
サーバ認証では証明書をサーバに設定するため、証明書を更新する場合にクライアント側の作業は必要ありません。一方、クライアント認証ではクライアントとサーバ両方の作業が必要となります。
また、対象のクライアント数が多くなるとクライアント証明書の更新を一斉に行うことが難しくなるため、新旧のクライアント証明書を使える並行運用期間を設ける必要があります。その期間中は両方のクライアント証明書を検証できるようにサーバを設定しておく必要があります。
クライアント証明書の更新作業
クライアント証明書に署名したCA証明書の有効期間により必要な作業が変わるため、長い場合と短い場合に必要な作業を説明します。
※2026/01/01時点の状態として説明します。
(1) CA証明書の有効期間が長い場合
CA証明書の有効期間が2年残っているため、既存のCAで新しいクライアント証明書を発行します。
(2) CA証明書の有効期間が短い場合
CA証明書の有効期間が残り半年のため新しいCAを作成し、そのCAで新しいクライアント証明書を発行します。
グラフではCA証明書の有効期間を3年、クライアント証明書の有効期間を1年というイメージで記載していますが、実際の有効期間は運用ポリシーに合わせる必要があります。
クライアント証明書の更新作業の例
「(1)CA証明書の有効期間が長い場合」の新クライアント証明書の発行とWebサーバの設定方法は通常のクライアント認証を設定する場合と変わらないため、「(2)CA証明書の有効期間が短い場合」の新クライアント証明書の発行とWebサーバの設定方法を説明します。
また説明では以下のツールを使用します。
| 作業 | ツール |
|---|---|
| 証明書の作成と署名 | OpenSSL |
| Webサーバ | nginx |
| クライアント | curl |
サーバ証明書の作成
検証用にlocalhostというサーバ名でWebサーバを立てるため、プライベートCAでサーバ証明書を発行します。
1.作業ディレクトリの作成
mkdir -p server_ca server
2.プライベートCAの秘密鍵と証明書を作成
# 秘密鍵を作成 openssl genrsa -out server_ca/ca.key 4096 # CA証明書を作成 openssl req -x509 -new \ -key server_ca/ca.key \ -days 1095 \ -sha256 \ -out server_ca/ca.pem \ -subj "/C=JP/O=Local Dev/OU=Dev CA/CN=local-dev-ca"
3.サーバの秘密鍵とサーバ証明書のCSRを作成
# 秘密鍵を作成 openssl genrsa -out server/localhost.key 2048 # CSRを作成 openssl req -new \ -key server/localhost.key \ -out server/localhost.csr \ -subj "/C=JP/O=Local Dev/CN=localhost" # SAN定義ファイルを作成 cat > server/server_san.ext <<'EOF' subjectAltName = DNS:localhost,IP:127.0.0.1 EOF
4.プライベートCAでサーバ証明書を発行する
openssl x509 -req \
-in server/localhost.csr \
-CA server_ca/ca.pem \
-CAkey server_ca/ca.key \
-CAcreateserial \
-out server/localhost.crt \
-days 365 \
-sha256 \
-extfile server/server_san.ext
クライアント証明書の作成
既存のCA証明書の有効期間が残り半年のため新しいCAを作成するというシナリオを再現するため、有効期間が半年と3年のCAを作成します。また、それぞれのCAでクライアント証明書を発行します。
1.作業ディレクトリの作成
mkdir -p client_ca client
2.新旧のCAの秘密鍵と証明書を作成
- 旧CAの秘密鍵と証明書を作成 - 証明書の有効期間は半年(180日)
※検証用に「残存期間が半年のCA」を再現するため、この旧CA証明書は-days 180で作成します。
# 旧CAの秘密鍵を作成 openssl genrsa -out client_ca/old_ca.key 4096 # 旧CAの証明書を作成 openssl req -x509 -new -key client_ca/old_ca.key \ -days 180 -sha256 \ -out client_ca/old_ca.pem \ -subj "/C=JP/O=Example Inc/OU=Client CA/CN=example-client-ca-v1"
- 新CAの秘密鍵と証明書を作成 - 証明書の有効期間は3年(1095日)
# 新CAの秘密鍵を作成 openssl genrsa -out client_ca/new_ca.key 4096 # 新CAの証明書を作成 openssl req -x509 -new -key client_ca/new_ca.key \ -days 1095 -sha256 \ -out client_ca/new_ca.pem \ -subj "/C=JP/O=Example Inc/OU=Client CA/CN=example-client-ca-v2"
3.クライアントの秘密鍵とクライアント証明書のCSRを作成
# 秘密鍵を作成 openssl genrsa -out client/client.key 2048 # クライアント証明書のCSRを作成 openssl req -new \ -key client/client.key \ -out client/client.csr \ -subj "/C=JP/O=Customer A/CN=customer-a" # SAN定義ファイルを作成 cat > client/client_san.ext <<'EOF' subjectAltName = URI:urn:customer:a EOF
4.新旧のCAでクライアント証明書を発行する
- 旧CAでクライアント証明書を発行する - 有効期間は半年(180日)
# シリアルファイルを作成 echo 01 > client_ca/old_ca.srl # クライアント証明書を作成 openssl x509 -req \ -in client/client.csr \ -CA client_ca/old_ca.pem \ -CAkey client_ca/old_ca.key \ -CAserial client_ca/old_ca.srl \ -out client/old_client.pem \ -days 180 -sha256 \ -extfile client/client_san.ext
- 新CAでクライアント証明書を発行する - 有効期間は1年(365日)
# シリアルファイルを作成 echo 01 > client_ca/new_ca.srl # クライアント証明書を作成 openssl x509 -req \ -in client/client.csr \ -CA client_ca/new_ca.pem \ -CAkey client_ca/new_ca.key \ -CAserial client_ca/new_ca.srl \ -out client/new_client.pem \ -days 365 -sha256 \ -extfile client/client_san.ext
nginxにサーバ証明書と新旧のCA証明書を設定
サーバ認証用にサーバの秘密鍵と証明書を設定します。また、新旧のクライアント証明書を検証できるように新旧のCA証明書も設定します。
1.秘密鍵と証明書を配置するディレクトリを作成
mkdir -p /etc/nginx/tls
2.サーバの秘密鍵と証明書を配置する
cp server/localhost.key /etc/nginx/tls cp server/localhost.crt /etc/nginx/tls
3.新旧のCA証明書を配置する
nginxの ssl_client_certificate にはCA証明書を1つしか設定できないため新旧のCA証明書を結合したファイルを作成します。
cat client_ca/old_ca.pem client_ca/new_ca.pem > /etc/nginx/tls/old_new_ca.pem
※ Nginx(OpenSSL)は、ssl_client_certificate で指定されたファイルの中に、提示されたクライアント証明書の署名元(Issuer)と一致するCA証明書が含まれているかを順番にチェックします。そのため、新旧のCA証明書を結合したファイルを指定すると新旧のクライアント証明書を検証できるようになります。
4./etc/nginx/nginx.confを作成する
cat > /etc/nginx/nginx.conf <<'EOF'
user nginx;
worker_processes auto;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
server {
listen 443 ssl;
server_name localhost;
# サーバの秘密鍵を設定する
ssl_certificate_key /etc/nginx/tls/localhost.key;
# サーバ証明書を設定する
ssl_certificate /etc/nginx/tls/localhost.crt;
# CA証明書を設定する
ssl_client_certificate /etc/nginx/tls/old_new_ca.pem;
# クライアント認証を有効にする
ssl_verify_client on;
location / {
return 200 "ok\n";
}
}
}
EOF
※既存の nginx.conf がある場合は、http コンテキストや server コンテキストの中に、ssl関連の各ディレクティブを追記してください。
5.nginxを起動する
systemctl start nginx
curlによるアクセス確認
- クライアント証明書を指定せずにWebサーバにアクセス
curl -s -o /dev/null -w "%{http_code}\n" \
--cacert server_ca/ca.pem \
https://localhost
400
※ サーバ証明書の検証のために --cacert オプションでサーバ証明書に署名したCA証明書を指定しています。
レスポンスコードが400となりクライアント証明書を指定しないとアクセスに失敗します(設定によってはレスポンスコードが400以外になる場合があります)。
- 旧クライアント証明書を指定してWebサーバにアクセス
curl -s -o /dev/null -w "%{http_code}\n" \
--cacert server_ca/ca.pem \
--cert client/old_client.pem \
--key client/client.key \
https://localhost
200
レスポンスコードが200となりアクセスに成功します。
- 新クライアント証明書を指定してWebサーバにアクセス
curl -s -o /dev/null -w "%{http_code}\n" \
--cacert server_ca/ca.pem \
--cert client/new_client.pem \
--key client/client.key \
https://localhost
200
レスポンスコードが200となりアクセスに成功します。
上記の結果から新旧のクライアント証明書の並行運用ができていることが分かります。
終わりに
ユーザの認証強化では多要素認証やパスキーを使った認証の方が便利なため、相互認証(mTLS)を使う機会は減っていると思います。
一方、システム間の連携やデバイス認証では今後も相互認証は使われていくと思いますので、相互認証を使用した運用の参考になれば幸いです。