オレオレ認証局とオレオレ証明書
自分の理解のための試行錯誤を書いている。要注意。途中で力尽きているので参考にならないと思う。
単純な自己署名証明書を作るだけなら認証局は要らない。
認証局を作ることで、正式な発行、無効化(失効)(revoke)手順を行うことができる。
つまり、認証局を作る動機は、自動発行か自動認証(少なくともその証明書が証明局が発行したことが分かること)か失効をちゃんとやることだけになる。
飽くまで証明は「公開鍵と秘密鍵が合致するかどうか(公開鍵暗号)」「信頼できる相手による署名かどうか(信頼チェーン)」でしかない。
オレオレ認証局とはOSやブラウザなどにルート証明書がビルトインされていない認証局のこと。
ルート認証局の証明書は本質的に自分で自分に署名する以外方法がない。
ルート認証局の証明書をインストールすることで、オレオレ認証局が発行したオレオレ証明書を受け入れることができるようになる。
要するに本質的には「その認証局を信用するかどうか」という問題であって、信用できる認証局であればよいという話。
OSやブラウザに設定されている認証局は、つまりOSやブラウザが最初からその認証局を信用している、というだけの話。
Let's Encrypt ができたので、サーバー証明書に関してはオレオレ証明書、オレオレ認証局は不要となった。
一方で、クライアント証明書はいまだ、高額な証明書発行をどこかに頼むか、無料でやりたいならオレオレ認証局が必要。
ほかの CA や Let's Encrypt の認証ができない環境では、結局まだオレオレ証明書の発行が必要。
公式の(中間)CA証明書はまず発行されない。(セキュリティ的に安全であるかどうかの監査が必要。高額な供託金。)
クライアント証明書を使いたいケース
特定のクライアントだけにアクセスを許可するようなケース。(ID、パスワードが信用ならんという人向け)
特定のアプリケーション(例: OpenVPN)などでクライアント証明書が要求されるケース。
CAスクリプトで十分では?
最近 CA.pl スクリプトは入っていないことがあるらしい。easy-ca が使えない。
将来的に自動化したい。
クライアント証明書の自動発行を考えると、CAスクリプトではダメ。
古い鍵と証明書は履歴として残しておきたい。
openssl.cnf で一応 CA ができるように作られているので、それでよいのでは?
openssl.cnf の中味を正しく理解することと、自動化の時には相対パス指定になってるのはややまずい。(カレントディレクトリを移動してから使えば良いが)
openssl.cnf は、単なる変数と OpenSSL が実際に解釈する変数(セクション指定の変数と、処理のパラメータそのものになる変数)とが混在して書かれているため、非常に分かりづらくなっている。
さらにコンテキストによって使われるセクションが異なるので、ますます分からなくなっている。
さらに、CA用と末端のユーザー用の設定が混ぜて書いてあるので、ますます分からない。
openssl.cnf は大文字と小文字を区別しない。
信頼できる証明書として登録された自己署名証明書で、1サーバーでも、SAN を必ず付けなければならなくなった。そうしないと安全な証明書と認められない。(重要)
安全な証明書と認められないと PWA を作ることができない。
ワンライナーでできるとしている例では SAN が設定されないのでうまく行かない。
OpenSSL でオレオレ認証局を作る。
どの OpenSSL が使われているのかの確認
code:console
$ which openssl
/usr/bin/openssl
PowerShell の場合
code:console
PS> gcm openssl | fl
OpenSSL のバージョン確認
使えるコマンドやアルゴリズムが違ったりするので結構重要。
code:console
$ openssl version
OpenSSL 1.1.1d-freebsd 10 Sep 2019
OpenSSL で(デフォルト時)設定を読むディレクトリの確認
code:console
$ openssl version -d
OPENSSLDIR: "/etc/ssl"
OpenSSL の各種基本設定の読み出し
code:console
$ openssl version -a
OpenSSL 1.1.1d-freebsd 10 Sep 2019
built on: reproducible build, date unspecified
platform: FreeBSD-amd64
options: bn(64,64) rc4(16x,int) des(int) idea(int) blowfish(ptr)
compiler: clang
OPENSSLDIR: "/etc/ssl"
ENGINESDIR: "/usr/lib/engines"
Seeding source: os-specific
署名のためにどの証明書を使うか
CA証明書の秘密鍵の生成
CA証明書の署名要求の生成
CA証明書への署名(証明書の生成)
中間CA証明書の秘密鍵の生成
中間CA証明書の署名要求の生成
中間CA証明書の生成
証明書の秘密鍵の生成
証明書の署名要求の生成
証明書の生成
素で実行するときのコマンド
openssl では、便利にするために、1つのコマンドですべてできるようになっているので混乱を招いている。
ca でも署名できるし、x509 でも署名できる。
req で秘密鍵の生成と自己署名もできたりする。
証明書関連で本当の意味で必須のコマンドは以下の4つのみ。
table:command
genpkey 秘密鍵の生成
req 証明書署名要求の生成
x509 証明書への署名
ca CA用ツール
署名には ca コマンドと x509 コマンドが使えるが、どちらをどう使うべきか?
x509 コマンドは config ファイルを受け付けない。
x509 コマンドは -days で現在時刻からの日数でしか指定できない。
よって、正式な署名の場合には ca コマンドを使うのが望ましい。
-help オプションを付けると、各コマンドで使えるオプション一覧を見ることができる。
証明書要求の生成に必要な最低の設定ファイル
一般的に使われる問い合わせ型(一般的な CSR 作成で使われている distinguished_name を作成する場合)
標準添付されている openssl.cnf で使用されているものの req 関連部分をそのままコピーペーストしたもの。
少なくとも、用意した秘密鍵と、この設定ファイルさえあれば、証明書は発行できる。(ここでの拡張属性はサーバー証明書用)
code:openssl_req_min_prompt.cnf
string_mask = utf8only
default_md = sha256
distinguished_name = req_distinguished_name
attributes = req_attributes
req_extensions = v3_req
countryName = Country Name (2 letter code)
countryName_default = AU
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
stateOrProvinceName_default = Some-State
localityName = Locality Name (eg, city)
0.organizationName = Organization Name (eg, company)
0.organizationName_default = Internet Widgits Pty Ltd
# we can do this but it is not needed normally :-)
organizationalUnitName = Organizational Unit Name (eg, section)
commonName = Common Name (e.g. server FQDN or YOUR name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
# SET-ex3 = SET extension number 3
challengePassword = A challenge password
challengePassword_min = 4
challengePassword_max = 20
unstructuredName = An optional company name
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
code:console
$ openssl req -new -config ./openssl_req_min_prompt.cnf -key privkey.pem -out csr.pem
challengePassword は書いたままが平文で送られる。この項目をどう使うかはCA側で決める事になっている。
平文で送られていることは openssh req -text -in csrファイル -noout で見ればわかる。
各種の嘘説明が平然と流れているので要注意。
この場合は、パスワードが一致しないなら、証明書の発行や取り消しをさせない、という使い方が示されている。
unstructuredName (OID: 1.2.840.113549.1.9.2) は、あくまで補助で人間が見る以外の何の確認にも使われない。
string_mask = utf8only は、UTF-8 で distinguished_name (DN) を書くかどうかという話。ASCIIが安全だが、それ以外を書くならば必須になる。
default_md はメッセージダイジェストのこと。
現在は sha256 を書くべき。(SHA-2)
default_md を書かなかったときに何がデフォルトなのかは openssl dgst /dev/null するとわかる。
code:console
$ openssl dgst /dev/null
SHA256(/dev/null)= e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
少なくとも OpenSSL 1.1.1 では sha256 がデフォルトのはず。
reqセクションの req_extensions は証明書要求の時に使われる。指定したセクションに書いたX.509 拡張属性が設定される。
-reqexts オプションで別のセクションを指定することができる。
これは CA が反映するかもしれないし、しないかもしれない。(あくまで要求であって、証明書に残すかどうかはCA次第)
subjectAltName (SAN)はここに含める必要がある。
reqセクションの x509_extensions は -x509 オプションで自己署名するときに使われる。指定したセクションに書いたX.509 拡張属性が設定される。
-extensions オプションで別のセクションを指定することができる。
自己署名しないなら、不要な項目
非問い合わせ型
prompt = no にすると、問い合わせなしでいきなり作ることができる。
code:openssl_req_min no_prompt.cnf
prompt = no
distinguished_name = req_distinguished_name
attributes = req_attributes
string_mask = utf8only
default_md = sha256
countryName = JP
stateOrProvinceName = Tokyo
localityName = Chiyoda-ku
organizationName = OreOre corp
commonName = oreore.example.com
code:console
$ openssl req -new -config ./openssl_req_min_no_prompt.cnf -key privkey.pem -out csr.pem
証明書要求の内容表示
code:console
$ openssl req -text -in csr.pem -noout
証明書要求に自己署名して証明書を作る。
トップの CA は自己署名するしかない。
証明書要求に署名して証明書を作る。
証明書要求に対して正しく署名するには ca コマンドを使う。
自己署名
code:console
$ openssl x509 -req -in csr.pem -signkey privkey.pem -out cert.pem
CAの場合、以下の指定が必要。
basicConstraints=CA:TRUE
CAの自己署名証明書の生成
code:root_ca.cnf
encrypt_key = no
utf8 = yes
string_mask = utf8only
prompt=no
distinguished_name = root_dn
x509_extensions = extensions
req_extensions = v3_req
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
# 国名 (2文字のコード)
countryName = JP
# 地域名 (例: 市区町村)
localityName = Tokyo
# 組織名 (例: 会社)
0.organizationName = Example Corp
# 証明書の名前
commonName = Example Corp CA
keyUsage = critical,keyCertSign,cRLSign
basicConstraints = critical,CA:TRUE
subjectKeyIdentifier = hash
code:console
$ openssl ca -selfsign -in ca_csr.pem -keyfile ca_prikey.pem -out ca_cert.pem
code:console
$ openssl req -x509 --days 3650 -key privkey.pem -out csr.pem
証明書の確認
code:console
$ openssl x509 -text -in cert.pem
オレオレ認証局の鍵を生成する。
鍵はどこに保存すべきか?
OS によって異なる。
鍵は外部に漏れてはならない。
ローカル環境で認証局を作るのが安全。
運用時には認証局の証明書だけがあればよい。
OpenSSL のインストールで作られた openssl.cnf を使うか、別の設定ファイルを使うかのどちらか。
オレオレ認証局の証明書発行要求(CSR)ファイルを生成する。
code:console
$ openssl
OpenSSL でオレオレクライアント証明書を作る。
critical は原則使わない。
critical があると、それに反する使い方をすると(本来は)エラーになる。
一部の壊れたアプリケーションが正しく解釈してくれない。
証明書を無理矢理流用できたりするが、そこまで厳格にする必要性はない。
そもそも信用するかどうかは証明書を受け取った側の判断によるもので、証明書を渡す側ではない。
X.509 拡張属性関連
code:txt
basicConstraints = CA:TRUE, pathlen:1
keyUsage = cRLSign, keyCertSign
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always, issuer:always
Authority Key Identifier
CAがどの秘密鍵で署名したのかを示すためのフィールド
CRL Distribution Points (2.5.29.31)の URL を埋め込む場合
X.509拡張属性の設定で crlDistributionPoints を設定する。
code:openssl.cnf
...
authorityInfoAccess (1.3.6.1.5.5.7.1.1)
code:openssl.cnf
OCSP (1.3.6.1.5.5.7.48.1)
1.3.6.1.5.5.7.48.1
クライアント認証では、本当のところ何が必要なのか?
req_extensions は req
メモ
オレオレCAとオレオレ証明書を作る方法が手順化されている。どうしてそうするかは書いていないので応用が利かない。