SSL証明書の更新でハマらないために！事前準備＆疎通確認の重要ポイント

# はじめに

SSL証明書の更新作業は定期的に発生しますが、__「どこから手をつければいいのか」__ __「更新後に何を確認すればよいのか」__ といった点で悩むことはありませんか？

本記事は、私が実際にSSL証明書の更新をする中で学んだことを踏まえ、抑えておきたいポイントについて解説します。

SSL証明書の更新をミスなくスムーズに進めたい方は、ぜひ参考にしてください！



# 事前確認
SSL証明書の更新で一番重要なのが、事前確認のフェーズです。
サーバーに配備する証明書に関する情報を事前に確認しておくことで、更新作業後に不具合が発覚することはほぼないでしょう。

具体的に確認する内容は以下です。
* __証明書の有効期限の確認__
* __使用する証明書の整合性の確認__
  * __配備予定の証明書チェーンの整合性__
  * __証明書と秘密鍵の整合性__

それでは、実際の確認方法について解説していきます。

:::note warn
※以降の解説では、ローカルのPCに以下が用意されていることを前提に解説しています。
```bash
各ドキュメントの用意
・サーバー証明書
・中間証明書
・秘密鍵

コマンド実行時に使用する変数の宣言（ターミナルで実施）
# 証明書のパス（例: local/certs/xxx/xxx.cer
SERVER_CERT=your/path/to/server.cert 
# 中間証明書のパス（例: local/certs/xxx/intermedite.cert
INTERMEDIATE_CERT=your/path/to/cer
# 秘密鍵のパス（例: local/certs/xxx/private.pem
KEY=your/path/to/key
```
:::

### 証明書の有効期限の確認
通常、エンドエンティティーのサーバー証明書は1年間の有効期限があります。
期限切れによるシステムの不具合を防ぐために、証明書の更新作業が発生します。
しかし、更新予定の証明書の有効期限がすでに切れていたり、予想していない日時だったりすると、後々大きな問題になるでしょう。

そこで大事なのが、有効期限の確認です。
実際に使用する証明書に対して、以下のコマンドを実行して確認してみましょう。
```bash:有効期限確認
openssl x509 -noout -dates -in ${SERVER_CERT}
```
```bash:実行結果
notBefore=Dec 22 00:00:00 2023 GMT
notAfter=Jan 11 23:59:59 2025 GMT
```
:::note info
__確認観点__
__・notAfter（有効期限）が意図した日時になっていること__
:::
出力結果はGMTでの表記になるので、必要に応じてJSTに変更して確認しましょう。


### 使用する証明書の整合性の確認
前提としてSSL証明書には、階層構造が存在し、階層の上位の証明書が下位の証明書を署名（信頼性を担保）する仕組みがあります。
この部分については以下の記事で分かりやすく解説されています。
https://ssl.sakura.ad.jp/column/difference-in-ssl/

#### 配備予定の証明書チェーンの整合性
更新する証明書が、上位に紐付く証明書から署名されたものでないと、信頼性の保証がされず通信エラーとなってしまいます。
そのため、 __更新する証明書とその上位に紐付く証明書の整合性を事前に確認する__ 必要があります。
そこで、下記の確認コマンドを実行しましょう。
```bash:サーバー証明書と中間証明書の整合性確認
diff <(openssl x509 -issuer_hash -noout -in ${SERVER_CERT}) <(openssl x509 -subject_hash -noout -in ${INTERMEDIATE_CERT})
```
:::note info
__確認観点__
__・コマンド実行後に何も出力されないこと__
:::
こちらのコマンドは、 __サーバー証明書のissuer（発行者）__ と __中間証明書のsubject（対象者）__ のそれぞれのハッシュ値を比較するというコマンドです。

上位と下位の証明書が対応している状態だと、これらのハッシュ値は一致するはずなので、コマンド実行後に何も出力されません。

しかし、ハッシュ値が異なる場合は、差分が出力されるので、サーバー証明書と中間証明書の整合性がとれていないと判断できます。
この時点で、使用する証明書が正しいものなのかを確認しましょう。
<br>

#### 証明書と秘密鍵の整合性
また、証明書と秘密鍵の整合性も事前に確認できるので、実施するのが望ましいでしょう。
```bash:証明書と秘密鍵の整合性確認
diff <(openssl x509 -noout -modulus -in ${SERVER_CERT}) <(openssl rsa -noout -modulus -in ${KEY})
```
:::note info
__確認観点__
__・コマンド実行後に何も出力されないこと__
:::
サーバー証明書のmodulus値と秘密鍵のmodulus値は一致しなければならないという原理を踏まえ、当該コマンドを実行した際の、出力結果の有無によって整合性を判断できます。

万が一、差分が出力された場合は、使用する証明書もしくは秘密鍵が正しいものかを確認しましょう。

# 証明書の更新
私のプロジェクトでは、SSL証明書の登録や管理は AWS Certificate Manager（以降、ACM）上でおこなっています。
更新部分については、今回の本筋とは外れるため、ざっくりとした更新の流れだけ触れておきましょう。

証明書の更新の流れは以下です。
- ACMで証明書のインポートをおこなう
- インポートした証明書をElastic Load Balancerに適用する

# 疎通確認
証明書の更新が完了したタイミングで、正常に更新されているか確認する必要があります。
以下のコマンドを実行し、返却されたレスポンスを確認しましょう。
```bash:疎通確認
# 実行時にはhogehogeを検証したいドメインに変える
openssl s_client -connect hogehoge.jp:443 -showcerts
```

```bash:実行結果
# コメント箇所は出力内容の補足のため、実際には出力されない
# プロジェクト情報や秘匿情報に関わる出力内容はマスクしている

CONNECTED(00000007)
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = root_cert_name
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = intermidiate_cert_name
verify return:1
depth=0 C = JP, ST = Tokyo, L = ****, O = "company_name", CN = *.hogehoge.jp
verify return:1
---
Certificate chain
# 以下、サーバ証明書情報が出力される
 0 s:C = JP, ST = Tokyo, L = ****, O = "company_name", CN = *.hogehoge.jp
   i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = intermidiate_cert_name
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Dec 22 00:00:00 2023 GMT; NotAfter: Jan 11 23:59:59 2025 GMT
-----BEGIN CERTIFICATE-----
# サーバ証明書のエンコードされた内容が出力される
-----END CERTIFICATE-----
# 以下、中間CA証明書情報が出力される
 1 s:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = intermidiate_cert_name
   i:C = US, O = DigiCert Inc, OU = www.digicert.com, CN = root_cert_name
   a:PKEY: rsaEncryption, 2048 (bit); sigalg: RSA-SHA256
   v:NotBefore: Nov  6 12:23:45 2017 GMT; NotAfter: Nov  6 12:23:45 2027 GMT
-----BEGIN CERTIFICATE-----
# 中間証明書のエンコードされた内容が出力される
-----END CERTIFICATE-----
---
Server certificate
subject=C = JP, ST = Tokyo, L = ****, O = "company_name", CN = *.hogehoge.jp
issuer=C = US, O = DigiCert Inc, OU = www.digicert.com, CN = intermidiate_cert_name
---
No client certificate CA names sent
Peer signing digest: SHA256
Peer signature type: RSA
Server Temp Key: ECDH, prime256v1, 256 bits
---
SSL handshake has read 3452 bytes and written 446 bytes
Verification: OK
---
New, TLSv1.2, Cipher is ECDHE-RSA-AES128-GCM-SHA256
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES128-GCM-SHA256
    Session-ID: ****
    Session-ID-ctx: 
    Master-Key: ****
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket: ****
    Start Time: 1709271484
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---
closed
```
### 確認観点
#### depth（証明書の階層構造）の確認
```bash
・
・
・
depth=2 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = root_cert_name
verify return:1
depth=1 C = US, O = DigiCert Inc, OU = www.digicert.com, CN = intermidiate_cert_name
verify return:1
depth=0 C = JP, ST = Tokyo, L = ****, O = "company_name", CN = *.hogehoge.jp
verify return:1
・
・
・
```
実行結果の前半部分には、証明書チェーンの情報が出力されます。

___depth=2：ルート証明書の情報___
___depth=1：中間CA証明書の情報___
___depth=0：サーバ証明書の情報___

表示された情報が2、1、0の連番になっていれば階層構造に問題がないと判断できます。
※depthの数字が全て0や、連番になっていない場合は正しいチェーンで証明書が設定されていないので注意しましょう。

:::note info
__確認観点__
__・depthが連番になっていること__
:::


#### Verify return code（疎通状況）の確認
```bash
    ・
    ・
    ・
    TLS session ticket lifetime hint: 86400 (seconds)
    TLS session ticket: ****
    Start Time: 1709271484
    Timeout   : 7200 (sec)
    Verify return code: 0 (ok)
    Extended master secret: yes
---
closed
```

証明書の検証プロセスに問題がなければ、Verify return codeの値は0が返却されます。
codeの値が0以外の場合はエラーが起こっており、エラー内容が併せて表示されます。

:::note info
__確認観点__
__・Verify return codeが0で返却されていること__
:::
<br>

__コード早見表__
|Verify return code|エラー内容|原因|対応策|
|-----------------|------------------|------------------|--------|
|0|ok|問題なし|-|
|10|certificate has ezpired|証明書の有効期限切れ|証明書を更新する|
|21|unable to verify the first certificate|証明書チェーンの不完全性|サーバーが正しい証明書チェーンを提供しているか確認する
|27|certificate not trusted|信頼できない証明書|公式な認証局の証明書を使用する|

# おわりに
SSL証明書の更新は一見、証明書を最新化するだけのようで、意外と留意すべき部分が多いことに気付かされました。
SSL通信の仕組みや、階層構造についてなど、理解すべき内容が複雑なだけに、必要最低限でここだけは確認しなければいけない部分について本記事でまとめてみました。
ご覧いただいた方のお役に立てれば嬉しいです。