前回: 今度こそopensslコマンドを理解して使いたい (1) ルートCAをスクリプトで作成する
前回はスクリプトでルートCAを作成する際に、識別名などの重要な設定値をコマンドラインで指定しましたが、それ以外はノーマルの設定ファイル(openssl.cnf)の値をそのまま使用していました。
今回はOpenSSLの設定ファイルを理解して、適切な設定ファイルを作れるようになることを目指します。
設定ファイルの構成
以降の文書中の引用箇所は、以下のOpenSSL公式ドキュメントからの抜粋+和訳です。
- 設定ファイル manpage(1.0.2): https://www.openssl.org/docs/man1.0.2/man5/config.html
デフォルトセクション
設定ファイルの先頭から最初の名前付きセクションの前までを「デフォルトセクション」と言う。
ノーマルのopenssl.cnf
では、ファイル先頭部分にある以下の3行が「デフォルトセクション」の内容になります。
HOME = .
RANDFILE = $ENV::HOME/.rnd
oid_section = new_oids
名前付きセクション
コマンド(openssl
のサブコマンド)がどの名前付きセクションを参照するかは、各コマンドのドキュメントに明記されています。例えばca
コマンドは[ ca ]
セクションを、req
コマンドは[ req ]
セクションを参照し、それらの中で別のセクションが指定されていればそちらも参照します。
-
ca
コマンド manpage(1.0.2): https://www.openssl.org/docs/man1.0.2/man1/openssl-ca.html -
req
コマンド manpage(1.0.2): https://www.openssl.org/docs/man1.0.2/man1/openssl-req.html
以下はノーマルのopenssl.cnf
で、ca
コマンドとreq
コマンドが参照するセクションです。
caコマンドを実行
├──[ ca ]
│ └──[ CA_default ]
│ ├──[ usr_cert ]
│ └──[ policy_match ]
└── デフォルトセクション
└──[ new_oids ]
reqコマンドを実行
├──[ req ]
│ ├──[ req_distinguished_name ]
│ ├──[ req_attributes ]
│ └──[ v3_ca ]
└── デフォルトセクション
└──[ new_oids ]
上記はあくまでもノーマルのopenssl.cnf
を使用した場合で、以下のような方法で自由にカスタマイズすることができます。
名前付きセクションを任意の名前で作ることができる
設定ファイル内の参照先セクションを変更することができる(例:
[ ca ]
セクション内のdefault_ca =
)-
コマンドラインオプションで参照先セクションを指定することができる
- 設定ファイルの定義をオーバーライドできる
- コマンドによって、設定ファイルのどの定義をオーバーライドできるかが異なる(例:
ca
コマンドの-name
オプションは設定ファイルのdefault_ca
をオーバーライドできる)
デフォルトセクションの値
名前付きセクションとデフォルトセクションに同じ名前の設定があった場合について、以下のように規定されています。
設定が参照される時、まず名前付きセクションが調べられ、見つからなければデフォルトセクションが調べられる。
例えば乱数ファイルRANDFILE
は、デフォルトセクションで以下のように定義されています。
RANDFILE = $ENV::HOME/.rnd
しかし[ CA_default ]
セクションでは以下の設定値が定義されているので、このセクションが参照された場合は下記の設定値が使用され、上記のデフォルト値は無視されます。
[ CA_default ]
RANDFILE = $dir/private/.rand
環境変数
$ENV::name
の書式を使うことで、環境変数を置き換えることがでる。
RANDFILE = $ENV::HOME/.rnd
という設定は、$ENV::HOME
の部分が環境変数$HOME
に置き換えられるので、RANDFILE
のパスは$HOME/.rnd
になります。
この書式を利用すれば、設定ファイル中の任意の設定値を外部から注入することができます。
例えばCA(認証局)のパスdir
の値は、以下のように[ CA_default ]
セクションの中でハードコーディングされています。
[ CA_default ]
dir = /etc/pki/CA # 全体の保存場所
certs = $dir/certs # 発行した証明書をどこに保存するか
このdir
の値を環境変数CA_DIR
を参照するように変更すれば、1つの設定ファイルで複数のパスを使用できるようになります。
[ CA_default ]
dir = $ENV::CA_DIR # 全体の保存場所(環境変数)
certs = $dir/certs # 発行した証明書をどこに保存するか
以下は別々のCAのパスをCA_DIR
に設定してからca
コマンドを実行する例です。
export CA_DIR="/etc/pki/CA/ca1.mydomain"
openssl ca ...
export CA_DIR="/etc/pki/CA/ca2.mydomain"
openssl ca ...
環境変数の安全な使い方
設定値に含めた環境変数が存在しないと、設定ファイルを読み込もうとした時にエラーが発生する。
上記の場合、以下のようにデフォルトセクション内にデフォルト値をハードコーディングしておけば、環境変数CA_DIR
がなければデフォルト値が使用されるので、エラーの発生を回避することができます。
CA_DIR = /etc/pki/CA # 全体の保存場所(環境変数がない場合のデフォルト値)
[ CA_default ]
dir = $ENV::CA_DIR # 全体の保存場所(環境変数)
certs = $dir/certs # 発行した証明書をどこに保存するか
その他の書式
全般的な書式に関するトピックを以下に抜粋します。
使用可能文字
セクション名には英数字とアンダースコアを使用できる。
name=value
のname
には、英数字と.
,
;
_
などいくつかの記号を使用できる。
変数名の重複
同一セクション内に同じ名前の変数がある場合、最後の値以外は無視される。
識別名のように同じフィールドが複数回定義されるような特殊な場合、通常は名前の先頭から.
までを無視することで回避している。以下の例はこの方法でフィールドOU
を2つ定義している。
1.OU="My first OU"
2.OU="My Second OU"
ノーマルのopenssl.cnf
では以下の箇所がこれに相当します。
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Default Company Ltd
# we can do this but it is not needed normally :-)
#1.organizationName = Second Organization Name (eg, company)
#1.organizationName_default = World Wide Web Pty Ltd
変数展開
$var
または${var}
の書式を含む値の文字列は変数展開され、同一セクション内の変数値で置き換えられる。また$section::name
か${section::name}
の書式を使えば、他のセクションの変数値で置き換えることもできる。
環境変数の出力
ENV::name
の書式を使うことで、値を環境変数に割り当てることもできる。
これは$ENV::name
の逆で、設定ファイルの値を環境変数に出力することもできるようです。
エスケープ文字
引用符か
\
を使って特殊文字をエスケープすることができる。
行末を\
にすれば複数行をまたぐことができる。
特殊文字\n
\r
\b
\t
が使用できる。
X509 V3 拡張設定
以降の文書中の引用箇所は、以下のOpenSSL公式ドキュメントからの抜粋+和訳です。
- X509 V3 拡張設定 manpage(1.0.2): https://www.openssl.org/docs/man1.0.2/man5/x509v3_config.html
critical
拡張セクション内の各行は以下の書式を取る:
extension_name=[critical,] extension_options
critical
があればその拡張設定は重要である。
critical
はプログラムが設定内容を厳守するように指定しますが、critical
がない場合の動作は各プログラムの実装に依存するようです。
厳密な運用を目指すなら積極的に指定するべきオプションですが、設定ファイルの[ v3_ca ]
セクションに以下のコメントが書かれているように、プログラムによっては正常動作を妨げる場合もあるようです。
# PKIXはこれを推奨しているが、いくつかの壊れたソフトウェアが「critical」拡張設定で固まってしまう。
basicConstraints = critical,CA:true
# だから私たちは代わりにこうする。
basicConstraints = CA:true
Basic Constraints(基本制約)
これは証明書がCA証明書かどうかを示す多値の拡張設定で、最初に
CA:{TRUE|FALSE}
の設定が必須である。CA:TRUE
の場合、pathlen
に正の数を設定することができる。
例:
basicConstraints=CA:TRUE
basicConstraints=CA:FALSE
basicConstraints=critical,CA:TRUE,pathlen:0CA証明書には
CA:TRUE
の値を設定したbasicConstraints
が必須である。
エンドユーザー証明書はCA:FALSE
とするか、またはこの拡張設定を完全に除外する必要があるが、中にはエンドエンティティ証明書にCA:FALSE
のbasicConstraints
を含むことを要求するソフトウェアもある。
pathlen
は証明書チェーン内でこのCAに連なることができるCAの最大数を示す。したがって、pathlen:0
のCAはエンドユーザー証明書への署名しかできず、他のCAに署名することはできない。
CA証明書にはbasicConstraints=CA:TRUE
が必要で、中間認証局を作る可能性があるなら上位CAの証明書にpathlen
オプションも必要です。
サーバー/クライアント証明書にはbasicConstraints
が必須ではないものの、上記の説明を読むと互換正のためにbasicConstraints=CA:FALSE
を入れておいた方が無難なようです。
Key Usage(鍵の用途)
これは許可される鍵の使い方の配列を設定する多値の拡張設定である。
設定できる値:digitalSignature
nonRepudiation
keyEncipherment
dataEncipherment
keyAgreement
keyCertSign
cRLSign
encipherOnly
decipherOnly
例:
keyUsage=digitalSignature, nonRepudiation
keyUsage=critical, keyCertSign
ドキュメントには詳細が記されていませんが、ノーマルのopenssl.cnf
には典型的な例として以下の記述があります。
- クライアント証明書用([ usr_cert ]):
- keyUsage = nonRepudiation, digitalSignature, keyEncipherment
- 証明書署名要求用([ v3_req ]):
- keyUsage = nonRepudiation, digitalSignature, keyEncipherment
- CA証明書用([ v3_ca ]):
- keyUsage = cRLSign, keyCertSign
Extended Key Usage(鍵の用途の拡張)
この拡張設定は、証明書の公開キーの用途を示す使用法(オブジェクトの短縮名かOUDのドット区切り数値)の配列で構成されている。以下のPKIX、NSおよびMSの値は特に意味がある。
値 意味
----- -------
serverAuth SSL/TLS Webサーバ認証
clientAuth SSL/TLS Webクライアント認証
codeSigning コード署名
emailProtection Eメール保護 (S/MIME)
timeStamping Trusted Timestamping
msCodeInd Microsoft Individual Code Signing (authenticode)
msCodeCom Microsoft Commercial Code Signing (authenticode)
msCTLSign Microsoft Trust List Signing
msSGC Microsoft Server Gated Crypto
msEFS Microsoft Encrypted File System
nsSGC Netscape Server Gated Crypto
例:
extendedKeyUsage=critical,codeSigning,1.2.3.4
extndedKeyUsage=nsSGC,msSGC
OpenVPNのサーバー/クライアントで試したところ、サーバー側の証明書にextndedKeyUsage = serverAuth
がないと、クライアント側から接続することができませんでした。
Subject Key Identifier(サブジェクト鍵識別子)
サブジェクト鍵識別子: 証明書の所有者の公開鍵を識別する値(ハッシュ値)
文字列値で
hash
を指定すれば、RFC3280のガイドラインに自動的に従う。これ以外の設定は推奨されない。
例:
subjectKeyIdentifier=hash
Authority Key Identifier(認証局鍵識別子)
認証局鍵識別子: 証明書に署名したCAの公開鍵を識別する値(ハッシュ値)
keyid
とissuer
の2つのオプションが使用可能で、どちらもオプション値always
を取ることができる。
keyid
オプションがある場合、サブジェクト鍵識別子を親証明書からコピーしようとする。 値always
がある場合、オプションが失敗するとエラーが返される。
issuer
オプションは、発行者の証明書から発行者とシリアル番号をコピーする。これはalways
フラグが常に値を含まない限り、keyid
オプションが失敗するか含まれない場合にのみ行われる。
例:
authorityKeyIdentifier=keyid,issuer
非推奨の拡張設定
これらはNetscape用の非標準設定で、すでにほぼ廃止されている。
Netscape文字列拡張
例:
nsComment
= "Some Random Comment"
このカテゴリの他の拡張設定:nsBaseUrl
nsRevocationUrl
nsCaRevocationUrl
nsRenewalUrl
nsCaPolicyUrl
nsSslServerName
Netscape証明書タイプ
これは証明書の使用目的を示すフラグの配列を設定する多値の拡張設定だが、現在はこれに代わって
basicConstraints
、keyUsage
およびextendedKeyUsage
の拡張設定が使用されている。
nsCertType
に使用可能な値:client
server
objsign
reserved
sslCA
emailCA
objCA
これらの設定を使用する例がよく紹介されていますが、上記の説明を読む限り、よほど古いソフトウェアを使わない限り必要なさそうです。
今後の予定
今回の内容を踏まえ、設定ファイルと前回作成したスクリプトを見直してルートCAを再作成する
-
作成したCAでサーバー/クライアント証明書を発行し、OpenVPNでの動作を確認する
- OpenVPN コミュニティ Wiki: https://community.openvpn.net/openvpn
証明書失効処理
記事一覧
今度こそopensslコマンドを理解して使いたい (1) ルートCAをスクリプトで作成する
今度こそopensslコマンドを理解して使いたい (2) 設定ファイル(openssl.cnf)を理解する
今度こそopensslコマンドを理解して使いたい (3) CA証明書の拡張設定を検証する
今度こそopensslコマンドを理解して使いたい (4) サーバー/クライアント証明書を一括生成する
今度こそopensslコマンドを理解して使いたい (5) CRL(証明書失効リスト)を作成してOpenVPNに配布する
今度こそopensslコマンドを理解して使いたい (補足1) サンプルスクリプトのまとめ