Java - HttpClient への SSL 実装での javax.net.ssl プロパティについて (+Quarkus 少々)
Java - HttpClient への SSL 実装での javax.net.ssl プロパティについて (+Quarkus 少々)
前提知識メモ
トラストストア
キーストア
ref: Java の SSLSocket で SSL クライアントと SSL サーバーを実装する
以下、簡単な実装例(トラストストア、キーストアのパスは直指定)
ref: 【Java】SSL 通信を実装する
javax.net.ssl に関して
■ javax.net.ssl.SSLContext
SSLSocketFactory.getDefault () で使用される SSLContext と SSLContext.getDefault()で得られる SSLContext には、以下のシステムプロパティが影響する (※SSLContext.setContext()が行われた倍は除く)
- javax.net.ssl.keyStore
- javax.net.ssl.keyStorePassword
- javax.net.ssl.trustStore
- javax.net.ssl.trustStorePassword
ref: Get SSLContext for default system truststore in Java(JSEE) - StackOverflow
■ javax.net.ssl.TrustManagerFactory & javax.net.ssl.X509TrustManager
注: null の KeyStore パラメータが SunJSSE の「PKIX」または「SunX509」TrustManagerFactory に渡される場合、ファクトリは次の手順でトラストデータを検索します。
refs:
- https://docs.oracle.com/javase/jp/7/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager
- https://docs.oracle.com/javase/jp/8/docs/technotes/guides/security/jsse/JSSERefGuide.html#X509TrustManager
ドキュメントの日本がおかしく正確なところは分からないが、恐らく TrustManagerFactory.getInstance() でアルゴリズムに「PKIX」または「SunX509」を指定した場合、javax.net.ssl.trustStore
等のシステムプロパティが指定されていればそれを優先して使用されると思われる
また、以下の通り
デフォルトのトラストマネージャーのアルゴリズムは「PKIX」です。
デフォルトアルゴリズムは「PKIX」と思われるため TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
で取得した TrustManagerFactory にはjavax.net.ssl.trustStore
等が適用されると思われる
HTTP Client ライブラリ編
Java の HTTP Client はいくつかあるが、今回は google-client-java-api を見ていく(理由は特にない。業務で使われていたため)
サンプル
SSLFactory sslFactory = SSLFactory.builder() .withIdentityMaterial("identity.jks", "password".toCharArray()) .withTrustMaterial("truststore.jks", "password".toCharArray()) .build(); NetHttpTransport httpTransport = new NetHttpTransport.Builder() .setSslSocketFactory(sslFactory.getSslSocketFactory()) .setHostnameVerifier(sslFactory.getHostnameVerifier()) .build(); // ref: https://sslcontext-kickstart.com/client/google.html
HttpTransport にはいくつか種類がある。
- NetHttpTransport:JDK の HttpURLConnection がベース (今回はこちらを見る)
- ApacheHttpTransport:Apache HttpClient がベース
- 他にもいくつか
NetHttpTransport.Builder を使用して証明書を設定できるようである。
特になにもセットしなければ、sslSocketFactory, hostnameVerifier は null となる。これらは null でなければ NetHttpTransport.buildRequest で、HttpURLConnection (https の場合は HttpsURLConnection) にセットされる。
javax.net.ssl.HttpsURLConnection に関しては以下の通り
このクラスでは、HostnameVerifier と SSLSocketFactory を使用します。どちらのクラスにも、デフォルトの実装が定義されています。 これらの実装は、クラスごと(static)またはインスタンスごとに置き換えることもできます。すべての新しい HttpsURLConnection のインスタンスには、生成時にデフォルトの static 値が割り当てられます。
SSLSocketFactory のデフォルトは、以下で取得できるものと思われる
javax.net.ssl.SSLSocketFactory.getDefault() に関しては以下の通り
このメソッドがはじめて呼び出されると、セキュリティ・プロパティ ssl.SocketFactory.provider が検査されます。null 以外の場合、その名前のクラスがロードされ、インスタンス化されます。 それ以外の場合、このメソッドは SSLContext.getDefault().getSocketFactory()を返します。この呼出しに失敗した場合は、使用できないファクトリが返されます。
SSLContext.getDefault() は、前述の通り javax.net.ssl.* システムプロパティが影響すると思われるため
NetHttpTransport.Builder に何もセットせず、すぐさま build() して得られる NetHttpTransport から生成する HttpRequest (HttpsURLConnection を保持) には、javax.net.ssl.* のシステムプロパティが適用されると思われる。
※推測の域を出ないため要確認
追記:javax.net.ssl.* のシステムプロパティでトラストストアは適用できた。
■ 蛇足:Apache HttpComponents HttpClient
ちょっと参考になりそうなのを見つけたためリンクのみ添付
Quarkus についてメモ
本件に関して調べ始めた発端。
Quarkus アプリケーション内 から 外部の HTTPS リソースにアクセスする場合は、以下等の quarkus のプロパティ(application.conf) は効かない
- quarkus.http.ssl.certificate.key-store-file
- quarkus.http.ssl.certificate.key-store-password
- quarkus.http.ssl.certificate.trust-store-file
- quarkus.http.ssl.certificate.trust-store-password
これらはあくまで、SSL を使用して Quarkus アプリケーション (サーバ) を公開する際に使用するためのプロパティ (ブラウザ等から Quarkus アプリケーションにアクセスする際に使用されるもの)
ref: プロパティ quarkus.http.ssl.certificate.key-store-file が機能せず、要求されたターゲットへの有効な証明書パスが見つからない
故に、実行時に javax.net.ssl.* のシステムプロパティでトラストストア等を適用する必要がありそう。 ※それでもダメだった場合はソース上でトラストストア等を直接読み込んで、上記で触れた google-client-java-api では NetHttpTransport.Builder にセットすれば適用できそう。
追記:javax.net.ssl.* のシステムプロパティでトラストストアは適用されたため、トラストストアを読み込んで NetHttpTransport.Builder にセットするような処理を自前で用意する必要はなかった。
また、認証局に OpenID Connect の認証を行うための仕組みが用意されており、OpenID Connect 時に使用する証明書を指定するためのプロパティが上記とは別に用意されている。
OPENID CONNECT (OIDC) AUTHORIZATION CODE FLOW MECHANISM
ただ、こちらのプロパティを使用すると、パスワードの指定も必須になってしまう。
OIDC の際も、上記プロパティの指定が無ければ、javax.net.ssl.* のシステムプロパティで適用されたトラストストアを使用し、こちらのケースではパスワード指定必須でないため、パスワード指定を避けたい場合はシステムプロパティを使用すると良い。
さいごに
SSL の実装を行ったことがこれまでなく、参考になりそうなとあるサービスのコードを見ていて、証明書検証有効時には特に証明書を読み込むような処理もなく、無効時には https://gist.github.com/kazuhira-r/bb3ca27bc6194ff4900e のような実質検証を行わないような実装となっており、Why? というところからスタートしてからここまで調べ、恐らく javax.net.ssl.truststore 等で証明書適用できる、できなかった場合の代替案としてどういう風にすれば適用できるかまで見れたのでひとまず良しとする。
推測があっていたかは後日追記するかもしれない。
追記:推測は合っていた