0. はじめに
この記事は、学んだ技術や知識についてを備忘録としてまとめることを目的にしている。今回はOpenSSLを用いたプライベートCA作成方法についてまとめる。
注意
この記事で作る証明書はすべて開発・検証専用です。本番環境では使わないでください。
記事が長くなったため、内容を5本に分けている。この記事は全体の目次であり、各章の本文は次の記事にある。
| 記事 | 収録する章 | 内容 |
|---|---|---|
| mTLSとは何か ― TLSとの違いから証明書チェーンまで | 2章 | 前提知識。手を動かさずに読む |
| OpenSSLでプライベートCAを作る ― 構築手順 | 3〜11章・13章 | 本編。ここで実際に証明書を作る |
| OpenSSLでプライベートCAを作る ― スクリプト全文 | 12章 | 手順をまとめたスクリプトの全文 |
| 【用語解説】OpenSSLでプライベートCAを作るときに出てくる用語 | 付録 | 用語の解説 |
章番号は5本を通して連番になっている。3章から11章まではcerts/配下を順に作っていく一続きの作業なので、記事を上から順に実行していけばよい。
なお、本文中に出てくる用語の解説は別記事にまとめてある。
本文中では次の形で参照する。
用語解説(別記事)
- G01. ○○とは
- G02. △△とは
1. 開発環境
- macOS: 26.6
- OpenSSL 3.6.3
- 出口: nginx(
ssl_client_certificateによるクライアント証明書検証) - 端末側: macOS
2. 基本概念
TLSとmTLSの違い、ハンドシェイクの流れ、証明書チェーンと信頼ストアのしくみを説明する。
3. 作成するCAの構成
これから作る証明書5枚と、その親子関係・有効期間を先に決めておく。
4. 事前準備
OpenSSL 3.xの用意、CAごとのディレクトリと台帳、CA設定ファイルを作る。
5. ルートCAを作成する
信頼の起点になるルートCAの鍵と自己署名証明書を作る。
6. 中間CAを作成する
中間CAの鍵とCSRを作り、ルートCAに署名してもらう。チェーンファイルもここで作る。
7. サーバ証明書を作成する
SANにホスト名を書いたサーバ証明書を、中間CAに署名してもらう。
8. クライアント証明書を作成する
端末に配るクライアント証明書を5枚発行し、.p12にまとめる。
9. 失効リスト(CRL)を作成する
証明書を1枚失効させ、失効リスト(CRL)を生成する。
10. 生成物を検証する
ここまでに作った証明書をopenssl verifyにかけ、チェーンと失効が効くことを確かめる。
11. nginxで動作確認する
nginxを立ててmTLSを有効にし、5枚のクライアント証明書が想定どおりの結果になるかを確認する。
12. スクリプト全文
ここまでの手順をまとめたシェルスクリプトの全文。
13. 運用上の注意
秘密鍵の扱いと、検証が終わったあとの後始末。
14. まとめ
プライベートCAの作成手順を整理すると、次の5段階になる。
-
台帳を用意する —
openssl caはファイルベースのCA運用ツール - ルートCAを自己署名で作る — 信頼の起点
- 中間CAを作る — CSRを作り、ルートCAで署名する
-
末端の証明書を作る — サーバ(SAN必須)とクライアント(
CNの値が識別子) - チェーンファイルにまとめる — 用途によって中身が違う
作業中に詰まった箇所を一覧にしておく。
| 章 | 症状 | 原因 |
|---|---|---|
| 4.1 | オプションが通らない | macOS標準のopensslはLibreSSL |
| 4.2 |
index.txtが無いと怒られる |
openssl caは台帳(index.txt / serial / crlnumber)が前提 |
| 7.3.3 | 拡張が入っていない証明書ができる |
-extfileを渡すと-extensionsの参照先がそのファイルになる |
| 7.3.2 |
CNを書いたのにホスト名照合が通らない |
サーバ証明書はSANしか見られない(RFC 9525) |
| 7.5.1 | 環境によって信頼されたりされなかったり |
ssl_certificateとssl_client_certificateに渡すPEMを取り違えている |
| 8.2.1 | 同じCNの値で2枚目が発行できない |
unique_subject = noが要る |
| 3.3 |
-daysに何を入れるか決まらない |
上限は業界ルールであり、プライベートCAには適用されない |
| 11.4 | すべてのクライアント証明書が拒否される |
ssl_crlはチェーンの全段のCRLを要求する。ルートCAのCRLが無いと中間CAの段で止まる |
証明書そのものより、openssl caが「ファイルベースのCA運用ツール」であるという前提を知っているかどうかで、かかる時間がだいぶ変わった。
15. 参考文献
記述の根拠にした資料である。(最終確認: 2026-09-15)
仕様(一次情報)
| 資料 | URL | この記事のどこで使ったか |
|---|---|---|
| RFC 8446 — The Transport Layer Security (TLS) Protocol Version 1.3 | https://www.rfc-editor.org/rfc/rfc8446.html | 2.2.3. mTLSのハンドシェイク(CertificateRequest / Certificate / CertificateVerify)、certificate_authorities拡張(§4.2.4, §4.3.2, §4.4) |
| RFC 9525 — Service Identity in TLS | https://www.rfc-editor.org/rfc/rfc9525.html | 7.3.2. サーバ証明書のCNを使ってはいけない根拠。2023年11月、RFC 6125を廃止 |
| RFC 6125 — Representation and Verification of Domain-Based Application Service Identity | https://www.rfc-editor.org/rfc/rfc6125.html | 7.3.2. CN非推奨化の経緯(RFC 9525に廃止された) |
| RFC 2818 — HTTP Over TLS | https://www.rfc-editor.org/rfc/rfc2818.html | 7.3.2. 同上。SANが無ければCNを使う、とされていた時代の規定 |
| RFC 7292 — PKCS #12: Personal Information Exchange Syntax v1.1 | https://www.rfc-editor.org/rfc/rfc7292.html | 8.4. .p12の仕様 |
| RFC 5280 — Internet X.509 PKI Certificate and CRL Profile | https://www.rfc-editor.org/rfc/rfc5280.html | 2.3 / 3.1. 証明書とCRLの構造、basicConstraints / keyUsage / pathlen
|
| RFC 2253 — Lightweight Directory Access Protocol (v3): UTF-8 String Representation of Distinguished Names | https://www.rfc-editor.org/rfc/rfc2253.html | 2.3. nginxの$ssl_client_s_dnが従うDNの表記形式 |
| RFC 5737 — IPv4 Address Blocks Reserved for Documentation | https://www.rfc-editor.org/rfc/rfc5737.html | 記事中の例で192.0.2.10を使っている理由 |
RFC 9525の該当箇所(引用)
The Common Name RDN MUST NOT be used to identify a service because it is not strongly typed (it is essentially free-form text) and therefore suffers from ambiguities in interpretation.
OpenSSL公式ドキュメント
| 資料 | URL | この記事のどこで使ったか |
|---|---|---|
openssl ca |
https://docs.openssl.org/master/man1/openssl-ca/ | 4.2 / 4.3 / 7.3 / 7.4 / 8.2 / 8.3 / 9. 台帳の必須性、unique_subjectの既定値、-extfileと-extensionsの関係、-startdate / -enddate、-revoke / -gencrl / -crlexts、ツールの位置づけ |
openssl pkcs12 |
https://docs.openssl.org/master/man1/openssl-pkcs12/ | 8.4. .p12の既定の暗号方式(AES-256-CBC + PBKDF2)とMAC(SHA256) |
openssl verification options |
https://docs.openssl.org/master/man1/openssl-verification-options/ | 11.1. -verify_depthの数え方 |
openssl caの記述(引用・要点)
-
database: "The text database file to use. Mandatory. This file must be present though initially it will be empty." -
new_certs_dir: "Mandatory. It specifies the directory where new certificates will be placed." -
-extfile: "An additional configuration file to read certificate extensions from (using its unnamed (default) section unless the -extensions option is also used)." -
-extensions: "The section of the configuration file containing certificate extensions to be added when a certificate is issued (defaults to x509_extensions unless the -extfile option is used)." - ツールの位置づけ: "This command was originally meant as an example of how to do things in a CA. Its code does not have production quality."
openssl verification optionsの記述(引用)
Limit the certificate chain to num intermediate CA certificates. A maximal depth chain can have up to num+2 certificates, since neither the end-entity certificate nor the trust-anchor certificate count against the -verify_depth limit.
nginx公式ドキュメント
| 資料 | URL | この記事のどこで使ったか |
|---|---|---|
| Module ngx_http_ssl_module | https://nginx.org/en/docs/http/ngx_http_ssl_module.html | 2.5 / 7.5 / 9 / 11. 各ディレクティブの意味と$ssl_client_*変数 |
| ngx_event_openssl.c(nginxのソース) | https://github.com/nginx/nginx/blob/master/src/event/ngx_event_openssl.c | 10.5. ssl_crlがX509_V_FLAG_CRL_CHECK_ALLを立てること |
該当箇所(引用)
-
ssl_certificate: "If intermediate certificates should be specified in addition to a primary certificate, they should be specified in the same file in the following order: the primary certificate comes first, then the intermediate certificates." -
ssl_client_certificate: "The list of certificates will be sent to clients. If this is not desired, thessl_trusted_certificatedirective can be used." -
ssl_trusted_certificate: "In contrast to the certificate set byssl_client_certificate, the list of these certificates will not be sent to clients." -
ssl_verify_depth: 既定値は 1 -
ssl_crl: "When using intermediate certificates, their CRLs should be specified in the same file."
証明書の有効期間
| 資料 | URL | この記事のどこで使ったか |
|---|---|---|
| Apple — Requirements for trusted certificates in iOS 13 and macOS 10.15 | https://support.apple.com/en-us/102028 | 3.3. 398日ルールと、その適用範囲(手動追加したルートCAは対象外) |
| CA/Browser Forum — Ballot SC-081v3 | https://cabforum.org/2025/04/11/ballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods/ | 3.3. 有効期間の上限が業界団体の投票で決まること、398日→47日への段階的短縮 |
Appleの記述(引用)
TLS server certificates issued on or after September 1, 2020 00:00 GMT/UTC must not have a validity period greater than 398 days.
This change will not affect certificates issued from user-added or administrator-added Root CAs.
この2文目が、「プライベートCAには適用されない」の根拠である。
macOSのopensslがLibreSSLであること
| 資料 | URL | この記事のどこで使ったか |
|---|---|---|
| LibreSSL — Wikipedia | https://en.wikipedia.org/wiki/LibreSSL | 4.1. LibreSSLがOpenSSL 1.0.1gからのフォークであること、ABI互換が無いこと |
| drduh/macOS-Security-and-Privacy-Guide Issue #356 | https://github.com/drduh/macOS-Security-and-Privacy-Guide/issues/356 | 4.1. macOSがLibreSSLを同梱しており、Homebrewのopensslがkeg-onlyである理由 |
手元での実行結果も併記しておく。
$ /usr/bin/openssl version
LibreSSL 3.3.6
本格的なCA運用ツール(参考)
| 資料 | URL |
|---|---|
| step-ca | https://smallstep.com/docs/step-ca/ |
| cfssl | https://github.com/cloudflare/cfssl |
二次情報(補足的に参照)
有効期間の段階的短縮の中間ステップ(200日 / 100日)については、CA/Browser Forumの公式投票ページに具体的な日付の記載が無かったため、以下を参照した。一次情報ではない点に注意が必要である。
| 資料 | URL |
|---|---|
| Sectigo — CA/B Forum Cuts SSL/TLS Certificate Lifespan to 47 Days | https://www.sectigo.com/resource-library/sectigo-cab-reduce-ssl-tls-certificates-lifespan-47-days |
| AppViewX — CA/Browser Forum Certificate Validity Changes | https://www.appviewx.com/blogs/ca-browser-forum-certificate-validity-changes/ |
一般向けの解説記事
| 資料 | URL |
|---|---|
| トランスポート層セキュリティー(TLS)とは — IBM | https://www.ibm.com/jp-ja/think/topics/transport-layer-security |