#はじめに
Amazon API GatewayのmTLS機能リリースに絡めて、以下の3つの記事を書きましたが、書いている中で私がTLSの証明書や、その検証がどう行われているのか、また、CAの証明書の更新期限が近づいた時の運用がどうなるのか?等、PKIの基本がわかっている方なら、あっさりと「そこ、なやむの?」といわれそうな部分ではありますが、色々復習しながら検証を進めました。
- Amazon API GatewayでmTLSを試してみた。(1/2)
- Amazon API GatewayでmTLSを試してみた。(2/2)
- ACMのPrivate CAを利用した場合のAmazon API GatewayのmTLS構成を試してみた。
#サマリ
- ルートCAや中間CAの秘密鍵が再生成されなければ、証明書更新(期限切れに備えた更新を想定)はクライアント証明書による接続に影響なし
- 期限切れしたCA証明書に署名されたクライアント証明書は接続できないが、CA証明書を更新しトラストストアに反映すれば接続可能
- 秘密鍵を更新したCA証明書をトラストストアに反映後は旧CA秘密鍵により署名されたクライアント証明書は接続不可
#おさらい
一連の記事を書く中で、OpenSSLコマンドを使っていくつかおさらいを実施ししました。特にぼんやりしていたのは、CA証明書の更新、秘密鍵の更新でした。ぼんやりとは理解していましたが、仕様等をちゃんと理解していなかった部分もあり、OpenSSLコマンドを利用して色々確認しました。
今回想定したこと
「ルートCAや中間CAの証明書の有効期限が切れた時、API Gateway側でmTLSのトラストストアとしてその証明書を利用してる場合、つまり、クライアント証明書が、その中間CAによって生成されていた場合どうなるのだろう?」という疑問から以下、登場人物とシチュエーションを想定し検証をしました。
登場人物
- ルートCA 6か月後に証明書期限切れ
- 中間CA1 3か月後に証明書期限切れ
- 中間CA2 3か月後に証明書期限切れ
- 中間CA3 3か月後に証明書期限切れ。秘密鍵の漏洩が発覚
- 中間CA4 既に証明書期限切れ
- WebAPI提供者 中間CA/ルートCAの証明書をトラストストアに格納。Amazon API Gatewayで提供
- クライアント 上記CAからクライアント証明書を発行。WebAPIに接続。
シチュエーション
- ルートCAは6か月後の証明書期限切れに備えて、1時間前に証明書を更新し公開した
- 中間CA2は3か月後の証明書期限切れに備えて、1時間前に証明書を更新した
- 中間CA3は鍵が漏洩したので、1時間前に証明書と秘密鍵を更新した。
- 中間CA4は期限切れを起こしてしまったので、1時間前に証明書を更新した。
- 中間CA2,3は証明書更新後に既に新規にクライアント証明書を発行している。
上記において現在、Amazon API GatewayのmTLS構成でクライアントは接続しているが、中間CA4が既に接続エラーを起こしていること、秘密鍵漏洩を中間CA3で発生しており、早急に対応が必要となった。
お断り:今回CRLの運用は実施していません。
確認したいポイント
1. API Gatewayのトラストストア更新前でも、新規に発行されたCA証明書を利用して生成されたクライアント証明書が接続できるか否か
2. CA証明書が更新されても秘密鍵が更新されていなければ以前のCA証明書で生成された、有効期間内のクライアント証明書が継続利用できること
3. ついでに確認したいこと(期限切れの証明書をAPI Gatewayには登録できるのか?)
4. ついでに確認したいこと(既に登録済みの期限が切れの中間証明書をAPI Gatewayは検証でどう扱うのか?)
#準備するもの
最初にOpenSSLで以下を準備
- ルートCA用の秘密鍵と証明書 更新あり
- 中間CA1用の秘密鍵と証明書: 更新なし
- 中間CA2用の秘密鍵と証明書: 更新あり
- 中間CA3用の秘密鍵と証明書: 秘密鍵更新あり
- 中間CA4用の秘密鍵と証明書: 期限切れ
- 中間CA1が発行するクライアント証明書
- 中間CA2が発行するクライアント証明書
- 中間CA3が発行するクライアント証明書
- 中間CA4が発行するクライアント証明書
途中で更新するもの
- ルートCAの証明書
- 中間CA2の証明書
- 中間CA3の秘密鍵/証明書
途中で追加するもの
- 中間CA2のクライアント証明書(CA更新後)
- 中間CA3のクライアント証明書(CA秘密鍵、CA証明書更新後)
上記準備するものについては、最初にOpenSSLを利用して全て作成しました。
作成コマンド
#Root CA Private Key
openssl genrsa -out rootCA.key 2048
#Root CA Self-sign Certificate
openssl req -x509 -new -key rootCA.key -sha256 -days 180 -out rootCA.pem -subj "/CN=Private ROOT CA CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
#Intermediate CA Private Key
openssl genrsa -out intermediateCA1.key 2048
openssl genrsa -out intermediateCA2.key 2048
openssl genrsa -out intermediateCA3.key 2048
openssl genrsa -out intermediateCA4.key 2048
#Intermediate CA CSR
openssl req -new -key intermediateCA1.key -out intermediateCA1.csr -subj "/CN=Private Inter CA1 CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
openssl req -new -key intermediateCA2.key -out intermediateCA2.csr -subj "/CN=Private Inter CA2 CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
openssl req -new -key intermediateCA3.key -out intermediateCA3.csr -subj "/CN=Private Inter CA3 CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
openssl req -new -key intermediateCA4.key -out intermediateCA4.csr -subj "/CN=Private Inter CA4 CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
#Intermediate CA Certificate signed by Root CA
openssl x509 -req -in intermediateCA1.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA1.pem -days 90
openssl x509 -req -in intermediateCA2.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA2.pem -days 90
openssl x509 -req -in intermediateCA3.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA3.pem -days 90
openssl x509 -req -in intermediateCA4.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA4.pem -days 0
#Client Secret Key
openssl genrsa -out client1.key 2048
openssl genrsa -out client2.key 2048
openssl genrsa -out client3.key 2048
openssl genrsa -out client4.key 2048
#Client CSR
openssl req -new -key client1.key -out client1.csr -subj "/CN=Client1"
openssl req -new -key client2.key -out client2.csr -subj "/CN=Client2"
openssl req -new -key client3.key -out client3.csr -subj "/CN=Client3"
openssl req -new -key client4.key -out client4.csr -subj "/CN=Clinet4"
#Client Certificate signed by Intermediate CA
openssl x509 -req -in client1.csr -CA intermediateCA1.pem -CAkey intermediateCA1.key -CAcreateserial -sha256 -out client1.pem -days 90
openssl x509 -req -in client2.csr -CA intermediateCA2.pem -CAkey intermediateCA2.key -CAcreateserial -sha256 -out client2.pem -days 90
openssl x509 -req -in client3.csr -CA intermediateCA3.pem -CAkey intermediateCA3.key -CAcreateserial -sha256 -out client3.pem -days 90
openssl x509 -req -in client4.csr -CA intermediateCA4.pem -CAkey intermediateCA4.key -CAcreateserial -sha256 -out client4.pem -days 1
トラストストアの置き換え(新規作成でもよい)
既に以前の記事でAWS の ACM Private CAを利用した証明書発行ならびにAPI Gatewayのトラストストア登録をしていましたので、その時に設定した中間CAやルートCAの証明書がトラストストアに登録されています。それを以下のコマンドで作成したトラストストアに置き換えます。
catで実行すると改行がはいらず形式が崩れるので awk 1
を利用しました。
awk 1 rootCA.pem \
intermediateCA1.pem \
intermediateCA2.pem \
intermediateCA3.pem \
intermediateCA4.pem \
> truststore
これをS3 にアップロードしてAPI Gatewayのトラストストア設定に反映するだけ。
# Upload してVersionIDを取得。
VersionId=$(aws s3api put-object --bucket mtlstest-truststores3bucket-6rdlcmrisggk --key truststore --body truststore | jq -r '.VersionId' )
# VersionIDを指定してトラストストアを更新。
aws apigatewayv2 update-domain-name \
--domain-name api.xxxx.xxxxx\
--domain-name-configurations CertificateArn=arn:aws:acm:ap-northeast-1:123456789012:certificate/123456-626f-48c5-b10b-fc51b4545730,EndpointType=REGIONAL \
--mutual-tls-authentication TruststoreUri=s3://mtlstest-truststores3bucket-6rdlcmrisggk/truststore,TruststoreVersion=$VersionId
サイド、以下のコマンドで接続確認をしたところ、Client1,Client2,Client3は接続できましたが、Client4はCA証明書が有効期限切れのため接続できないようでした。
curl --key client1.key --cert client1.pem https://api.XXXX.XXXX/hoge/auth
curl --key client2.key --cert client2.pem https://api.XXXX.XXXX/hoge/auth
curl --key client3.key --cert client3.pem https://api.XXXX.XXXX/hoge/auth
curl --key client4.key --cert client4.pem https://api.XXXX.XXXX/hoge/auth
念のため、Client4の証明書を確認しましたが以下の通りでした。
date; openssl x509 -in client4.pem -text -noout
Sat Sep 26 11:32:03 UTC 2020
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
71:c6:98:60:13:a4:42:fa:42:4f:af:30:10:5f:01:f2:0e:41:2b:ad
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Private Inter CA4 CN, O = Sample Org, OU = Sample Ou, L = Meguro, ST = Tokyo, C = JP
Validity
Not Before: Sep 26 11:31:35 2020 GMT
Not After : Sep 27 11:31:35 2020 GMT
Subject: CN = Clinet4
Subject Public Key Info:
現在時刻をdateコマンドで最初に表示しています。ご覧いただいた通り、クライアント証明書はNot Before とNot Afterの時刻の間にあり、有効な範囲内ですが、CA証明書が有効期間内ではありません。以下、CA証明書の情報の抜粋です。
date; openssl x509 -in intermediateCA4.pem -text -noout
Sat Sep 26 11:32:29 UTC 2020
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
76:9c:ad:af:75:af:6f:15:70:ac:ba:d0:6e:d8:cd:34:78:96:4a:08
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = Private ROOT CA CN, O = Sample Org, OU = Sample Ou, L = Meguro, ST = Tokyo, C = JP
Validity
Not Before: Sep 26 11:31:34 2020 GMT
Not After : Sep 26 11:31:34 2020 GMT
Subject: CN = Private Inter CA4 CN, O = Sample Org, OU = Sample Ou, L = Meguro, ST = Tokyo, C = JP
Subject Public Key Info:
検証開始
期限切れCAの証明書更新
期限切れのCA証明書を利用したクライアント認証は成功しないため、ここではClient4による接続を回復するために期限切れCAの証明書更新を更新します。
以下のコマンドで期限を延ばして再度証明書を生成し、トラストストアを更新しました。前回の記事でも書きましたが、Amazon API Gatewayでは、クライアント証明書に署名をした中間CAだけでなく、上位のCA(ルートCAまでのチェーン)の公開鍵証明書をトラストストアに格納する必要がありますが、同一サブジェクト名の証明書は1つしか置けません。したがって、期限切れ証明書を取り除いた有効なCA証明書を結合(今回はawkで結合しています)して配置し、設定を更新しました。
運用を考えると、ルートCA、中間CAの有効な証明書を個別に保持しそれを必要な都度、結合して登録するのが良いのではないでしょうか?(都度、中身をけして、追加するという作業はミスと有無と思います)
#Intermediate CA Certificate signed by Root CA
openssl x509 -req -in intermediateCA4.csr -CA rootCA.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA4Updated.pem -days 360
awk 1 rootCA.pem \
intermediateCA1.pem \
intermediateCA2.pem \
intermediateCA3.pem \
intermediateCA4Updated.pem \
> truststore
VersionId=$(aws s3api put-object --bucket mtlstest-truststores3bucket-6rdlcmrisggk --key truststore --body truststore | jq -r '.VersionId' )
aws apigatewayv2 update-domain-name \
--domain-name api.horiba.work \
--domain-name-configurations CertificateArn=arn:aws:acm:ap-northeast-1:123456789012:certificate/12345678-626f-48c5-b10b-fc51b4545730,EndpointType=REGIONAL \
--mutual-tls-authentication TruststoreUri=s3://mtlstest-truststores3bucket-6rdlcmrisggk/truststore,TruststoreVersion=$VersionId
設定が反映された後、動作確認をしたところ、Client4で接続が確認できました。
期限切れ間近の証明書の更新
ここでの確認点は、CA証明書更新後に新たに生成されたクライアント証明書は、トラストストア更新前でも接続可能か?という点です。
既にClient4の検証を通じて、CA証明書が更新されても過去に生成されたクライアント証明書で接続できることは確認しました。ここでは逆を確認したいと思います。
ただ、秘密鍵は更新せず、証明書の更新のみなので影響ないことを念のため示す形になります。
ここでは
- rootCA
- Intermidediate2
を更新し、新しい証明書を利用したクライアント証明書の作成を実施したいと思います。
#Update Root CA Self-sign Certificate
openssl req -x509 -new -key rootCA.key -sha256 -days 365 -out rootCAUpdated.pem -subj "/CN=Private ROOT CA CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
#Update Intermediate CA Certificate signed by Root CA
openssl x509 -req -in intermediateCA2.csr -CA rootCAUpdated.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA2Updated.pem -days 180
上記は、ルートおよび中間CAを更新した状態です。ただし、まだ、API Gatewayには反映していません。反映前に以下のコマンドで新しく秘密鍵を作り、証明書を発行し、接続確認をしてみました。過去の鍵も含め、以下の5つのCurlコマンドは全て接続に成功しました。
署名は秘密鍵を利用して行っているため、秘密鍵を変えない限り、影響は受けないのだと理解しています。
#generate new Client
openssl genrsa -out client2new.key 2048
openssl req -new -key client2new.key -out client2new.csr -subj "/CN=Client2new"
#Client Certificate signed by Intermediate CA
openssl x509 -req -in client2new.csr -CA intermediateCA2Updated.pem -CAkey intermediateCA2.key -CAcreateserial -sha256 -out client2new.pem -days 90
curl --key client1.key --cert client1.pem https://api.xxxxx.xxx/hoge/auth
curl --key client2.key --cert client2.pem https://api.xxxxx.xxx/hoge/auth
curl --key client3.key --cert client3.pem https://api.xxxxx.xxx/hoge/auth
curl --key client4.key --cert client4.pem https://api.xxxxx.xxx/hoge/auth
curl --key client2new.key --cert client2new.pem https://api.xxxx.xxx/hoge/auth
さて、トラストストア内のルートCAおよび、中間CAのintermediateCA2の証明書を最新の証明書に更新するために以下のコマンドを実施しておきます。
awk 1 rootCAUpdated.pem intermediateCA1.pem intermediateCA2Updated.pem intermediateCA3.pem intermediateCA4Updated.pem > truststore
VersionId=$(aws s3api put-object --bucket mtlstest-truststores3bucket-6rdlcmrisggk --key truststore --body truststore | jq -r '.VersionId' )
aws apigatewayv2 update-domain-name \
--domain-name api.XXXX.XXXX \
--domain-name-configurations CertificateArn=arn:aws:acm:ap-northeast-1:123456789012:certificate/12345678-626f-48c5-b10b-fc51b4545730,EndpointType=REGIONAL \
--mutual-tls-authentication TruststoreUri=s3://mtlstest-truststores3bucket-6rdlcmrisggk/truststore,TruststoreVersion=$VersionId
上記設定を実施後も過去に発行されたクライアント証明書で問題なく接続できました。公開鍵証明書が更新されても期限切れでなければ動作することを確認できました。さて問題はこの次です。中間CAの秘密鍵が漏洩した場合は??です。
CAの秘密鍵漏洩による、秘密鍵の更新とCA証明書更新
今までは公開鍵のペアとなる秘密鍵は更新されなかったので期限切れを除いて影響を受けることなく更新ができました。ここでは、有効期限は切れていないが秘密鍵が更新、つまり、公開鍵が更新される場合の動きを確認したいと思います。
まず、以下のコマンドでIntermediateCA3の新秘密鍵の生成、新CSRの生成、そして証明書の生成を行いました。また、合わせて、新証明書を利用した新クライアント証明書の生成も行いました。
#Intermediate CA Private Key
openssl genrsa -out intermediateCA3new.key 2048
#Intermediate CA CSR
openssl req -new -key intermediateCA3new.key -out intermediateCA3new.csr -subj "/CN=Private Inter CA3 CN/O=Sample Org/OU=Sample Ou/L=Meguro/ST=Tokyo/C=JP"
#Intermediate CA Certificate signed by Root CA
openssl x509 -req -in intermediateCA3new.csr -CA rootCAUpdated.pem -CAkey rootCA.key -CAcreateserial -sha256 -out intermediateCA3new.pem -days 90
openssl genrsa -out client3new.key 2048
#Client CSR
openssl req -new -key client3new.key -out client3new.csr -subj "/CN=Client3 new"
#Client Certificate signed by Intermediate CA
openssl x509 -req -in client3new.csr -CA intermediateCA3new.pem -CAkey intermediateCA3new.key -CAcreateserial -sha256 -out client3new.pem -days 90
上記の状態で、過去に作成したクライアント証明書と今回作成したClient3new.pemを利用した接続確認を行ったところ、Client3new.pem以外は接続できました。
では、今回作成した新IntermediateCA3証明書をトラストストアに登録したいと思います。
以下のコマンドで、CA証明書を連結し、トラストストアに反映しました。
awk 1 rootCAUpdated.pem intermediateCA1.pem intermediateCA2Updated.pem intermediateCA3new.pem intermediateCA4Updated.pem > truststore
VersionId=$(aws s3api put-object --bucket mtlstest-truststores3bucket-6rdlcmrisggk --key truststore --body truststore | jq -r '.VersionId' )
aws apigatewayv2 update-domain-name \
--domain-name api.xxxx.xxxx\
--domain-name-configurations CertificateArn=arn:aws:acm:ap-northeast-1:123456789012:certificate/12345678-626f-48c5-b10b-fc51b4545730,EndpointType=REGIONAL \
--mutual-tls-authentication TruststoreUri=s3://mtlstest-truststores3bucket-6rdlcmrisggk/truststore,TruststoreVersion=$VersionId
上記のコマンドが反映された後に接続確認を以下のコマンドで実施しました。結果はclient3は秘密鍵が更新されたことにより、接続エラーとなり、Client3newは無事接続できました。
curl --key client1.key --cert client1.pem https://api.xxxx.xxxx/hoge/auth
curl --key client2.key --cert client2.pem https://api.xxxx.xxxx/hoge/auth
curl --key client3.key --cert client3.pem https://api.xxxxx.xxxx/hoge/auth
curl --key client4.key --cert client4.pem https://api.xxxxx.xxxx/hoge/auth
curl --key client2new.key --cert client2new.pem https://api.xxxxx.xxxx/hoge/auth
curl --key client3new.key --cert client3new.pem https://api.xxxxxx.xxxxxx/hoge/auth
まとめ
冒頭で、以下のことを確認したいこととしてあげました。結果を書きたいと思います。
1. API Gatewayのトラストストア更新前でも、新規に発行されたCA証明書を利用して生成されたクライアント証明書が接続できるか否か
接続できる。新規に発行された証明書のSubjectが同じで、秘密鍵も変わっていなければ接続できる。
2. CA証明書が更新されても秘密鍵が更新されていなければ以前のCA証明書で生成された、有効期間内のクライアント証明書が継続利用できること
秘密鍵が更新されていなければ、接続可能
3. ついでに確認したいこと(期限切れの
証明書をAPI Gatewayには登録できるのか?)`
トラストストアに登録はできた。
4. ついでに確認したいこと(既に登録済みの期限が切れの中間証明書をAPI Gatewayは検証でどう扱うのか?)
クライアント証明書を利用して接続する際にエラーになった。