公開鍵証明書-プライベート認証局(オレオレ認証局)を作り、証明書チェーンを作成するまでの流れ

11 min read読了の目安(約9900字

本記事の目的

SSL/TSL通信や、アプリ開発を行う過程で証明書まわりの知識や実装スキルが必要となります。
現在私は仕事でスマホ決済アプリのAPI開発を行っているが、Appleとのプロビジョニング機能実装において、証明書チェーンの検証実装と、ローカル環境での動作確認やテストを行うにあたり、証明書を作成する必要があったのでそれをまとめました。
※Windows環境

これを機に証明書についての理解を深めましょう!

公開鍵証明書の概要

公開鍵証明書とは

簡単に言えば、Aさんの公開鍵Aがあったとし、「その公開鍵が確かにAさんのものであると証明する」ものになります。
公開鍵証明書にはAさんの名前や所属、メールアドレスなどの個人情報、Aさんの公開鍵が記載されており、認証局(certificate authority : CA)によるデジタル署名が行われています。

X.509証明書

X.509証明書(X.509 Certificate)は公開鍵証明書の標準フォーマットのことを指します。
現在、公開鍵証明書と言われれば基本的にまずはこの形式を想像すれば問題はなさそうです。

X.509証明書の構造化は下記の通りとなります。
IPAサイトより抜粋

作成する証明書チェーン

今回作成する証明書チェーンは、リーフ証明書(所謂サーバ証明書)、中間証明書、ルート証明書の三組とし、証明書のつながりを簡単にまとめると以下の通りです。
(1)末端のサーバー証明書(公開鍵証明書)を、中間認証局の秘密鍵を用いて署名する。
(2)中間認証局の公開鍵証明書を、最上位の認証局(ルートCA)の秘密鍵を用いて署名する。
(3)ルートCAの公開鍵証明書は、自身の秘密鍵を用いて自己署名を行う。

OpenSSLのダウンロード

ローカル環境で証明書を作成するためには、OpenSSLをダウンロードして使えるようにしておく必要があります。もしまだインストールされていない場合は、インストールを行ってから証明書の作成に移りましょう。
インストール手順は下記サイトが参考になります。

https://www.atmarkit.co.jp/ait/articles/1601/29/news043.html

ルート証明書の作成

前準備

証明書の作成に移る前に、フォルダ構成を整える必要があります。
任意の場所に以下のようなフォルダ構成を作成してください。
ここで、openssl.cnfファイルですが、今回はOpenSSLをダウンロード際に作成されたフォルダのbinにあるopenssl.cnfをそのままコピーして使用することとします。
例えば、私のローカル環境の場合だと、C:\Program Files\OpenSSL-Win64\bin\cnf\配下にこのファイルが作成されています。これをコピーして今回新たに作成したフォルダにペーストしましょう。

ssl/
┝ demoCA/
    └ certs/
    └ crl/
    └ newcerts/
    └ private/
┝ openssl.cnf

openssl.cnfを開くとわかるのですが、こちらには証明書の作成などを行う際の様々な設定が記載されています。特に今は変更する必要はありません。

次に作成したsslフォルダ配下に移動して、その他必要なファイルを下記コマンドで用意します。

$ echo 00 > ./demoCA/serial
$ echo 00 > ./demoCA/crlnumber
$ touch ./demoCA/index.txt

これで最低限必要な準備は完了です!
ルート証明書を作成していきましょう!

プライベート認証局(CA)を立てる

ここからプライベート認証局(ルート証明書)の作成を行っていきます。
まず、自己署名証明書と秘密鍵を生成します。 -daysオプションで有効期限を設定できるので、任意の日付を設定しましょう。今回はRSA秘密鍵を作成します。
※ここで作成される秘密鍵cakey.pemと証明書cacert.pemの名称をこれ以外にしてしまうと、後続の処理が上手く動かないです。理由はopenssl.cnfファイルでCAとする証明書とその秘密鍵の名称をcasert.pem、cakey.pemと設定しているためです。もしこれらの名前を変更したい場合は、openssl.cnfの対象箇所の設定も同時に編集しましょう。

$ cd demoCA
$ openssl req -new -x509 -newkey rsa:2048 -out cacert.pem -keyout private/cakey.pem -days 365 

このコマンド実行後、パスフレーズを二回要求されるので、任意のものを入力してEnterを押しましょう。すると以下のような入力求められるので、順に入力していきます。
Emailの入力はしなくても大丈夫です。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:Test
Common Name (e.g. server FQDN or YOUR name) []:myCA
Email Address []:

このコマンド完了後、以下のファイルが作成されていることがわかります。

  • demoCA/cacert.pem -> CA証明書
  • demoCA/private/cakey.pem -> 秘密鍵

以上でルート証明書の作成は完了です、意外に簡単ですね!

サーバ証明書の作成

続いて、サーバ証明書を作成していきます。
手順としては、3ステップになります。

  1. 秘密鍵を作成する。
  2. 秘密鍵を元に、証明書要求を作成する。
  3. 認証局で署名をし、サーバ証明書を作成する。

まずは秘密鍵を作成します。その前に0ステップ目として別のディレクトリを用意しておき、そこに格納していきましょう。

$ mkdir ssl/sub
$ cd ssl/sub

秘密鍵を作成します。RSA秘密鍵を下記コマンドで作成します。

$ openssl genrsa -out subkey.pem 2048

続いて作成した秘密鍵を元に、証明書要求(CSRファイル)を作成していきます。
これは認証局に対して、サーバ証明書への署名を申請するファイルになります。

$ openssl req -new -sha256 -key subkey.pem > subcsr.pem

また証明書情報を要求されるので、入力していきます。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:Test sub
Common Name (e.g. server FQDN or YOUR name) []:myCA Sub
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

コマンド完了後、以下のファイルが作成されます。

  • sub/subkey.pem -> RSA秘密鍵
  • sub/subcsr.pem -> CSRファイル

最後に認証局で署名を行い、証明書を完成させます。
sslフォルダ直下に戻り、以下コマンドを実行します。

Tips:設定ファイルを上手く読み込めていない場合は、下記のようにオプションで直接対象のcnfファイルを指定することで上手くいくと思います。

openssl ca -config ./openssl.cnf -in ./sub/subcsr.pem -out subcert.pem

認証局のパスフレーズが再び聞かれるので、入力してEnter押下します。
その後下記のようなログが出て、署名するかどうかを聞かれるので"y"を入力してEnterを押下し、処理を完了させます。

Signature ok
Certificate Details:
        Serial Number: 1 (0x1)
        Validity
            Not Before: May 30 02:08:02 2021 GMT
            Not After : May 30 02:08:02 2022 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = Company
            organizationalUnitName    = Test Sub
            commonName                = myCA Sub
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                61:C2:97:84:2A:19:F4:BB:42:C4:58:6C:C1:FF:0A:3C:14:F4:6D:33
            X509v3 Authority Key Identifier:
                keyid:39:77:97:10:84:A0:81:07:AD:FF:C0:1E:D1:97:66:6B:7E:C6:28:12

Certificate is to be certified until May 30 02:08:02 2022 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

コマンド完了後、以下のファイルが作成されています。

  • subcert.pem -> 認証局で署名したサーバ証明書

以上でサーバ証明書を作成することができました!

中間認証局の作成とサーバ証明書(リーフ証明書)の作成

さて、ここから先の需要は少なそうですが、今回私が最終的に作りたかったのは、三階層の証明書チェーンです(ルート証明書 > 中間証明書 > リーフ証明書)。
そこで、先ほど作成したサーバ証明書を半ば強引に中間認証局と機能させて、新たに証明書の署名を行いたいと思います。

新たに最初に作成したフォルダ構成と同じものを作成します。手順としては、ssl2フォルダを作成し、最初に作成したdemoCAフォルダとopenssl.cnfを丸ごとコピーして、ssl2配下にペーストします。※フォルダ名はなんでも構いません、わかりやすいように調整してください。
その後、private直下においてある秘密鍵を、先ほど作成したサーバ証明書の秘密鍵(subkey.pem)に、demoCA直下のcacert.pemを中間証明書(subcert.pem)に置き換えます。

ssl/
┝ demoCA/
    └ certs/
    └ crl/
    └ newcerts/
    └ private/cakey.pem
    └ cacert.pem
    └ index.xtx
    └ serial
    └ crlnumber
┝ openssl.cnf

ssl2
┝ demoCA/
    └ certs/
    └ crl/
    └ newcerts/
    └ private/subkey.pem
    └ subcert.pem
    └ index.xtx
    └ serial
    └ crlnumber
┝ openssl.cnf

次にopenssl.cnfの設定値を少し書き変えます。
【修正前】

...
...
[ CA_default ]
...
certificate	= $dir/cacert.pem 	# The CA certificate
...
private_key	= $dir/private/cakey.pem# The private key
...

【修正後】

...
...
[ CA_default ]
...
certificate	= $dir/subcert.pem 	# The CA certificate
...
private_key	= $dir/private/subkey.pem# The private key

ここから先は、中間証明書の作成時とほとんど同じ手順です。秘密鍵の作成>CSRファイルの作成>署名 の手順で進めます。
まず秘密鍵を作成します。今回は楕円曲線暗号を使用した秘密鍵を作成していきたいと思います。ecparam -list_curves コマンドで使用できる曲線一覧を見ることができます。

$ openssl ecparam -list_curves
$ openssl ecparam -genkey -name prime256v1 -out leafkey.pem

CSRファイルを作成します。

$ openssl req -new -sha256 -key leafkey.pem > leafcsr.pem

証明書情報を要求されるので、入力していきます。

Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Tokyo
Locality Name (eg, city) []:City
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Company
Organizational Unit Name (eg, section) []:Test Leaf
Common Name (e.g. server FQDN or YOUR name) []:myCA Leaf
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

コマンド完了後、以下のファイルが作成されます。

  • leafkey.pem -> ECDSA秘密鍵
  • leafcsr.pem -> CSRファイル

最後に下記コマンドで署名を行います。

$ openssl ca -config ./openssl.cnf -in leafcsr.pem -out leafcert.pem

Signature ok
Certificate Details:
        Serial Number: 2 (0x2)
        Validity
            Not Before: May 30 02:25:44 2021 GMT
            Not After : May 30 02:25:44 2022 GMT
        Subject:
            countryName               = JP
            stateOrProvinceName       = Tokyo
            organizationName          = Company
            organizationalUnitName    = Test Leaf
            commonName                = myCA Leaf
        X509v3 extensions:
            X509v3 Basic Constraints:
                CA:FALSE
            Netscape Comment:
                OpenSSL Generated Certificate
            X509v3 Subject Key Identifier:
                C1:46:C6:9A:8E:27:E0:D3:DF:8D:4F:72:08:1F:16:3C:72:27:92:90
            X509v3 Authority Key Identifier:
                keyid:61:C2:97:84:2A:19:F4:BB:42:C4:58:6C:C1:FF:0A:3C:14:F4:6D:33

Certificate is to be certified until May 30 02:25:44 2022 GMT (365 days)
Sign the certificate? [y/n]:y


1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
Data Base Updated

これで無事リーフ証明書の作成が完了し、ルート証明書 > 中間証明書 > リーフ証明書の証明書チェーンを作成することができました。
次の章でファイル形式の変換(der形式へ変換)を行い、証明書の中身を見てましょう。

ファイル形式の変換と証明書の内容確認

今回作成した証明書はxxx.pemというPEM形式で出力していました。もちろんこのままテキスト形式で中身を見ることは可能なのですが、今回はDER形式に変換して証明書の内容を確認していきたいと思います。

変換する前に、各証明書のファイル形式について簡単におさらい。
まず、証明書ファイルはおそらく.crt, .cer, .pem, .derのどれかに属すると思います。
ここで、.crt, .cerは単にファイルの種類を表しているだけで、中身は.pemもしくは.derの形式になっています。
.pem, .derはどちらもASN.1というデータ構造で鍵を定義しており、この内容をシリアライズ化(バイナリー化)したものがDER形式、BASE64化したものがPEM形式になります。

さて、作成した各PEM形式のファイルをDER形式に変換してみましょう。
コマンドは下記の通りです。

$ openssl x509 -outform der -in cacert.pem -out cacert.der
$ openssl x509 -outform der -in subcert.pem -out subcert.der
$ openssl x509 -outform der -in leafcert.pem -out leafcert.der

コマンド完了後、以下のファイルが作成されます。

  • cacert.der
  • subcert.der
  • leafcert.der

試しにcasert.derをダブルクリックして開いてみると、

無事見ることができました!バイナリー形式なのでテキスト見ることはできませんが、このようにWindowsの場合だと画面に表示してくれます。

おまけ

cacert.der、subcert.derファイルをそれぞれインストールして、PCのお使いのユーザにおいて信頼させます。その後、leafcert.derを開き、証明書のパスを見てみると下記のようになっていることがわかります。
求めていた証明書チェーンが作成されていることが一目でわかりますね!

最後に

ここまで見ていただきありがとうございます。
需要が多いとは思いませんが、何らかの動作確認、単体テスト等で急に証明書チェーンの検証が必要になったときの手助けになればと思います。
今後は証明書まわりをもう少し深堀りした記事や、この作成した証明書チェーンをJAVAで実際にコードで検証していく記事を書きたいと思います。

では、またお会いしましょう!

参考記事

https://datatracker.ietf.org/doc/html/rfc5280
https://tex2e.github.io/rfc-translater/html/rfc5280.html
https://www.ipa.go.jp/security/pki/033.html
https://qiita.com/TakahikoKawasaki/items/4c35ac38c52978805c69#45-証明書の内容確認