JavaでHTTPS通信時のSSL証明書の失効チェックを有効にする

HTTPS通信を行う際に利用されるSSL証明書の失効チェックについて、質問されてデフォルトの挙動を即答することができなかった。

Javaで失効チェックを利用するための設定方法を調べたのでまとめておく。

 

 

[SSL証明書の失効チェック]

SSL証明書には有効期限が設定されているが、例えば証明書の秘密鍵が漏洩してしまったなどの理由で有効期限が来る前に証明書が失効する場合がある。

ssl.sakura.ad.jp

 

このような失効を確認する方法として、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つの設定を行う必要がある。

  1. VM引数でcom.sun.net.ssl.checkRevocation=trueを設定する
  2. いずれかの方法でセキュリティプロパティを変更する
    • セキュリティプロパティファイル(java.security)にocsp.enabled=trueの行を追加する
    • アプリケーション内でjava.security.Security.setProperty("ocsp.enabled", "true");を呼び出す

 

セキュリティプロパティファイルは、利用するJavaのバージョンや種類によって配置場所が微妙に異なるので注意が必要である。

 

また、CRL通信を利用するには、以下の2つの設定を行う必要がある。

  1. VM引数でcom.sun.net.ssl.checkRevocation=trueを設定する
  2. VM引数でcom.sun.security.enableCRLDP=trueを設定する

 

つまり、設定を行わないデフォルトの状態(Javaをインストールしただけの状態)では、SSL証明書の失効チェックは行われない。これは、失効チェックを行うことでパフォーマンスに影響があることも影響しているようだ。

blogs.oracle.com

blog.cloudflare.com

 

[デバッグログで見る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)を実施していない場合、AlgorithmCheckerBasicCheckerのログは出力されるが、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引数も設定されていないという状態のため、このような例外が発生しているものと思われる。

orablogs-jp.blogspot.com

 

[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アプリケーションが実行される前に、アプリケーションに関連付けられた証明書が失効していないことがチェックされます。

  

[まとめ]

  • SSL通信時の証明書失効チェックは、Javaの場合デフォルトでは無効となっている
  • SSL通信時の証明書失効チェックをJavaで有効にする設定方法をまとめた
  • 実際の失効チェック結果の詳細はデバッグログを出力して確認できる
  • サンプルコードは以下のリポジトリにまとめた

github.com