susutex’s blog

自転車、電子工作、プログラミングとかについて書くかもしれないブログ

OpenSSLを使って証明書作成メモ

テスト用の証明書を作りたくなりopensslをいじっていたので方法のメモ。
なお、以下を調べ終わった後にどうやらCAを構築するなら証明書の生成にはopenssl x509コマンドを使うよりもopenssl caコマンドを使ったほうがよさそうなことに気づいた。
今のところ以下で目的は果たせそうなのでopenssl caの使い方についてはまたそのうち必要になったら調べることにする。

想定

rootCA->intermediateCA->serverというchainで証明書を作って検証する
署名アルゴリズムはECDSA with SHA256、curveはprime256v1
生成するファイルの拡張子はどうでもいいようなので、鍵ファイルは.key、証明書ファイルは.cer、CSRは.csrという名前で作成することにする。

1. 利用可能なcurveのリスト取得

以下で利用可能なcurveのリストを取得できる。prime256v1があったのでこれを使用する。

openssl ecparam -list_curves

2. 鍵ペアの生成と表示

まずは各CAおよびサーバの鍵ペアを作成しないと始まらない。以下で生成する。

openssl ecparam -genkey -name prime256v1 -out rootCA_key_pair.pem.key
openssl ecparam -genkey -name prime256v1 -out intermediateCA_key_pair.pem.key
openssl ecparam -genkey -name prime256v1 -out server_key_pair.pem.key

上記で生成されるファイルはkey pairが入っている。今回は使用しないがpublic keyのみを取り出す場合は以下コマンド。

openssl ec -in rootCA_key_pair.pem.key -outform PEM -pubout -out rootCA_public.pem.key

生成したkey pairおよびpublic keyは以下のようにして内容を確認できる

openssl ec -text -noout -in rootCA_key_pair.pem.key
openssl ec -text -noout -pubin < rootCA_public.pem.key

3. root certificateの作成

rootCAは自身のprivate keyを使用して自身のpublic keyを含む証明書に署名を行い自己署名証明書を生成する。
以下で自己署名証明書の生成及びその内容の表示ができる。

openssl req -new -x509 -key rootCA_key_pair.pem.key > rootCA_cert.pem.cer
openssl x509 -text -noout -in rootCA_cert.pem.cer

証明書生成時に色々聞かれるが、とりあえず全て何も入力せずenterを押しても証明書は生成される。
証明書の内容を表示すると自己署名なのでIssuerとSubjectが同じになり、そこにcertificate生成時に入力した情報が記載されることになる。

何も入力せずenterを押し続けた場合IssuerとSubjectにデフォルトで設定された値が入っているが、これらが下位の証明書と同じだとおかしいので何か値を設定したい。
テスト用なのでとりあえずC(country)=JP、CN(common name)=rootCAとしてこれらのみ入力した証明書を改めて作成してみる。生成時に対話的に入力してもよいが、-subjオプションで入力することもできる。
以下でIssuerとSubjectを設定した自己署名証明書を再度生成してその内容を表示する。

openssl req -new -x509 -subj /C=JP/CN=rootCA -key rootCA_key_pair.pem.key > rootCA_cert.pem.cer
openssl x509 -text -noout -in rootCA_cert.pem.cer

4. intermediate CAおよびserverのCSR作成

intermediateCAおよびserverは上位のCAにpublic keyを送って証明書を発行してもらうわけだが、その際に使用するのがCSR(certificate signing request)。
CSRはpublic keyだけでなく主体者(Subject)などの情報を含んでおり、上位CAはこれを受け取ることでそれらを含んだ証明書を作成できる。
以下コマンドでCSRの生成とその内容の表示を行う。

openssl req -new -key intermediateCA_key_pair.pem.key -outform PEM -subj /C=JP/CN=intermediateCA -out intermediateCA_csr.pem.csr -sha256
openssl req -new -key server_key_pair.pem.key -outform PEM -subj /C=JP/CN=server -out server_csr.pem.csr -sha256
openssl req -text -noout -in intermediateCA_csr.pem.csr
openssl req -text -noout -in server_csr.pem.csr

CSRは発行者の秘密鍵で自己署名されている。
試しに署名を破壊したCSRを生成して後述の5での証明書作成を実行しようとするとCertificate request self-signature did not match the contentsと出てエラーになった。

5. intermediate CAおよびserverの証明書作成

上記で作成したCSRに上位CAのprivate keyで署名し証明書を作成する。
以下で証明書作成を行いその内容を表示。

openssl x509 -req -in intermediateCA_csr.pem.csr -days 365 -CA rootCA_cert.pem.cer -CAkey rootCA_key_pair.pem.key -out intermediateCA_cert.pem.cer
openssl x509 -req -in server_csr.pem.csr -days 365 -CA intermediateCA_cert.pem.cer -CAkey intermediateCA_key_pair.pem.key -out server_cert.pem.cer
openssl x509 -text -noout -in intermediateCA_cert.pem.cer
openssl x509 -text -noout -in server_cert.pem.cer

証明書情報を表示すると証明書に署名した上位CAのSubjectがIssuerとして記載されていることがわかる。-daysで証明書作成時点からの有効期限を設定できる。

6. 証明書の検証

証明書がそろったので検証を行ってみる。
以下コマンドでrootCA証明書を使ってintermediateCA証明書を検証できる

openssl verify -CAfile rootCA_cert.pem.cer intermediateCA_cert.pem.cer

さらにrootCA->intermediateCA->serverというchainを検証するには以下コマンドを使うが、5で生成したintermediateCA証明書ではエラーが出るため7で作り直す必要がある。

openssl verify -CAfile rootCA_cert.pem.cer -untrusted intermediateCA_cert.pem.cer server_cert.pem.cer

7. intermediateCA証明書へのX.509 extensions追加

6のコマンドでserverまでの証明書検証をパスさせるにはintermediate CA証明書にX.509 extensionsというものを追加する必要があるらしい
参考
https://www.golinuxcloud.com/add-x509-extensions-to-certificate-openssl/
rootCA証明書もserver証明書も本来はそれぞれ必要な値があるようだが、6のコマンドでの検証はintermediateCAにのみ追加するだけでパスできるのでまずはそれを追加する。

Intermediate Certificate Extensionsに記載のとおりbasicConstraintsを追加する必要があるので以下内容のファイルをopenssl_intermediateCA_cert.cnfとして作成する。

[v3_ca]
basicConstraints        = critical,CA:TRUE,pathlen:0

pathlenはこのCAより下位に存在できるCAの数らしい。下位にはserverしかなくCAは存在しないので0でOK。
CSRから証明書を生成する際に作成したconfig fileを指定してextensionが追加された証明書を生成して表示する。

openssl x509 -req -in intermediateCA_csr.pem.csr -days 365 -CA rootCA_cert.pem.cer -CAkey rootCA_key_pair.pem.key -out intermediateCA_cert.pem.cer -extensions v3_ca -extfile openssl_intermediateCA_cert.cnf
openssl x509 -text -noout -in intermediateCA_cert.pem.cer

X509v3 extensionsから始まる部分が増える。Basic Constraintsのみを指定した場合もSubject Key IdentifierおよびAuthority Key Identifierが自動で追加された。
このintermediateCA証明書を使用すれば今度は以下コマンドが成功する。

openssl verify -CAfile rootCA_cert.pem.cer -untrusted intermediateCA_cert.pem.cer server_cert.pem.cer

8. 独自OIDのX.509 extensions追加

独自OIDのX.509 extensionsの追加を行う必要があったのでその方法もメモしておく。
7においてopenssl_intermediateCA_cert.cnfを以下のように編集しておくと独自のOIDがextensionに追加される。

[v3_req]
1.2.3.4.56 = ASN1:SEQUENCE:myoid_sequence
basicConstraints        = critical,CA:TRUE,pathlen:0

[myoid_sequence]
 field1 = BOOLEAN:TRUE
 field2 = OID:commonName
 field3 = UTF8:Third field

値は他に書き方があるかもしれないがASN1を使用して記載するとうまくいったのでこれを使用している。
https://www.openssl.org/docs/manmaster/man3/ASN1_generate_nconf.html
openssl x509で内容を確認すると拡張OIDのフィールドはバイナリをそのままasciiで表示しているようでほとんどピリオドになり内容がわからない。
内容確認には以下webサイトが役立つ。証明書ファイルを開くと表示されるbase64エンコードされた文字列を入力すると証明書の構造が表示でき、上記でmyoid_sequenceとして記載した構造が追加されていることが分かる。
https://lapo.it/asn1js/