HTTPS通信を行う際に利用されるSSL証明書の失効チェックについて、質問されてデフォルトの挙動を即答することができなかった。
Javaで失効チェックを利用するための設定方法を調べたのでまとめておく。
[SSL証明書の失効チェック]
SSL証明書には有効期限が設定されているが、例えば証明書の秘密鍵が漏洩してしまったなどの理由で有効期限が来る前に証明書が失効する場合がある。
このような失効を確認する方法として、Certificate Revocation List(証明書失効リスト、以下CRL)と、Online Certificate Status Protocol(オンライン証明書状態プロトコル、以下OCSP)の2つがある。
Javaでこれらの失効チェックを利用するにはいくつか設定を行う必要がある。
設定本体を説明する前に、OSなどの環境情報を記載しておく。VS CodeのRemote - Containers拡張機能を利用している。
# cat /etc/os-release PRETTY_NAME="Debian GNU/Linux 10 (buster)" NAME="Debian GNU/Linux" VERSION_ID="10" VERSION="10 (buster)" VERSION_CODENAME=buster ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" # java -version openjdk version "11.0.8" 2020-07-14 OpenJDK Runtime Environment 18.9 (build 11.0.8+10) OpenJDK 64-Bit Server VM 18.9 (build 11.0.8+10, mixed mode)
OCSP通信を利用するには、以下の2つの設定を行う必要がある。
- VM引数で
com.sun.net.ssl.checkRevocation=true
を設定する - いずれかの方法でセキュリティプロパティを変更する
- セキュリティプロパティファイル(
java.security
)にocsp.enabled=true
の行を追加する - アプリケーション内で
java.security.Security.setProperty("ocsp.enabled", "true");
を呼び出す
セキュリティプロパティファイルは、利用するJavaのバージョンや種類によって配置場所が微妙に異なるので注意が必要である。
- Java 8 (JDK)の場合:$JAVA_HOME/jre/lib/security/java.security
- Java 8 (JRE)の場合:$JAVA_HOME/lib/security/java.security
- Java 11の場合:$JAVA_HOME/conf/security/java.security
また、CRL通信を利用するには、以下の2つの設定を行う必要がある。
つまり、設定を行わないデフォルトの状態(Javaをインストールしただけの状態)では、SSL証明書の失効チェックは行われない。これは、失効チェックを行うことでパフォーマンスに影響があることも影響しているようだ。
[デバッグログで見るOCSP/CRL通信]
単にHTTPS通信を実行するだけでは、OCSP通信やCRL通信が実行されているのか分かりにくいので、デバッグログを出力してみる。
セキュリティのトラブルシューティングによると、VM引数にjava.security.debug=certpath
もしくはjava.security.debug=all
を設定するとデバッグログを出力できる。
OpenJDKで失効チェックを行う場合、RevocationCheckerクラスが利用される。
jdk/RevocationChecker.java at master · openjdk/jdk · GitHub
失効チェックを行う設定(com.sun.net.ssl.checkRevocation=true
)を実施していない場合、AlgorithmCheckerやBasicCheckerのログは出力されるが、RevocationCheckerクラスからのログが出力されない。
OCSPを有効にしている場合は、次のようなログが出力される。
certpath: -Using checker7 ... [sun.security.provider.certpath.RevocationChecker] certpath: RevocationChecker.check: checking cert SN: a8ce2c93 1dab1f97 02000000 007a4bab Subject: CN=www.google.com, O=Google LLC, L=Mountain View, ST=California, C=US Issuer: CN=GTS CA 1O1, O=Google Trust Services, C=US certpath: connecting to OCSP service at: http://ocsp.pki.goog/gts1o1core certpath: OCSP response status: SUCCESSFUL certpath: OCSP response type: basic certpath: Responder ID: byKey: 98D1F86E10EBCF9BEC609F18901BA0EB7D09FD2B certpath: OCSP response produced at: Fri Oct 02 11:33:22 UTC 2020 certpath: OCSP number of SingleResponses: 1 certpath: thisUpdate: Fri Oct 02 11:33:21 UTC 2020 certpath: nextUpdate: Fri Oct 09 11:33:21 UTC 2020 certpath: Status of certificate (with serial number 224380820526468432188609696160055708587) is: GOOD certpath: OCSP response is signed by the target's Issuing CA certpath: Constraints.permits(): SHA256withRSA Variant: tls server certpath: Verified signature of OCSP Response certpath: OCSP response validity interval is from Fri Oct 02 11:33:21 UTC 2020 until Fri Oct 09 11:33:21 UTC 2020 certpath: Checking validity of OCSP response on Sat Oct 03 14:18:04 UTC 2020 with allowed interval between Sat Oct 03 14:03:04 UTC 2020 and Sat Oct 03 14:33:04 UTC 2020 certpath: -checker7 validation succeeded
CRLを有効にしている場合は、次のようなログが出力される。
certpath: -Using checker7 ... [sun.security.provider.certpath.RevocationChecker] certpath: RevocationChecker.check: checking cert SN: a8ce2c93 1dab1f97 02000000 007a4bab Subject: CN=www.google.com, O=Google LLC, L=Mountain View, ST=California, C=US Issuer: CN=GTS CA 1O1, O=Google Trust Services, C=US certpath: RevocationChecker.checkCRLs() ---checking revocation status ... certpath: RevocationChecker.checkCRLs() possible crls.size() = 0 certpath: RevocationChecker.checkCRLs() approved crls.size() = 0 certpath: DistributionPointFetcher.getCRLs: Checking CRLDPs for CN=www.google.com, O=Google LLC, L=Mountain View, ST=California, C=US certpath: Trying to fetch CRL from DP http://crl.pki.goog/GTS1O1core.crl certpath: CertStore URI:http://crl.pki.goog/GTS1O1core.crl certpath: Downloading new CRL... certpath: DistributionPointFetcher.verifyCRL: checking revocation status for SN: a8ce2c93 1dab1f97 02000000 007a4bab Subject: CN=www.google.com, O=Google LLC, L=Mountain View, ST=California, C=US Issuer: CN=GTS CA 1O1, O=Google Trust Services, C=US certpath: Constraints.permits(): SHA256withRSA Variant: tls server certpath: Returning 1 CRLs certpath: RevocationChecker.checkApprovedCRLs() starting the final sweep... certpath: RevocationChecker.checkApprovedCRLs() cert SN: 224380820526468432188609696160055708587 certpath: -checker7 validation succeeded
ちなみに、OCSPを有効化せずに、com.sun.net.ssl.checkRevocation=true
とだけ設定し、com.sun.security.enableCRLDP=true
を設定しない場合、次の様な例外が発生する。
Caused by: java.security.cert.CertPathValidatorException: Could not determine revocation status at java.base/sun.security.provider.certpath.RevocationChecker.buildToNewKey(RevocationChecker.java:1110) at java.base/sun.security.provider.certpath.RevocationChecker.verifyWithSeparateSigningKey(RevocationChecker.java:930) at java.base/sun.security.provider.certpath.RevocationChecker.checkCRLs(RevocationChecker.java:603) at java.base/sun.security.provider.certpath.RevocationChecker.checkCRLs(RevocationChecker.java:464) at java.base/sun.security.provider.certpath.RevocationChecker.check(RevocationChecker.java:366) at java.base/sun.security.provider.certpath.RevocationChecker.check(RevocationChecker.java:336) at java.base/sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:125) ... 34 more
Javaの失効チェックはオプションが有効な場合、デフォルトではまずOCSPでの失効チェックを行い、問題が発生した場合にCRLにフォールバックする仕組みとなっている。上記の例外発生時は、OCSP通信実行のためのセキュリティプロパティが設定されておらず、CRL通信実行のためのVM引数も設定されていないという状態のため、このような例外が発生しているものと思われる。
[Oracle Javaのコントロールパネル]
ちなみに、Javaの証明書失効チェックで検索していると次のページが表示されることがある。
https://www.java.com/ja/download/help/revocation_options.html
この記事の冒頭にはこのように書かれている。
セキュリティを強化するために、Java 7 Update 25以降では証明書失効チェック機能がデフォルトで有効になっています。
先ほど本記事内で「設定を行わないデフォルトの状態(Javaをインストールしただけの状態)では、SSL証明書の失効チェックは行われない」と書いたが、これはあくまでHTTPS通信時の証明書についての失効チェックである。
上記サイトでは続けて以下のように記載されており、こちらはJavaアプレットやJava Web Startアプリケーションに関する証明書であることが分かる。
Javaが署名付きアプリケーションを起動しようとする前に、関連付けられた証明書が発行元の認証局によって取り消されていないことが確認されます。
(略)
署名付きアプレットまたはJava Web Startアプリケーションが実行される前に、アプリケーションに関連付けられた証明書が失効していないことがチェックされます。