1. ホーム
  2. java

[解決済み] HTTPS/SSL上のJavaクライアント証明書

2022-09-01 11:58:32

質問

私はJava 6を使用しています。 HttpsURLConnection をリモートサーバーに対して、クライアント証明書を使用して作成しようとしています。

サーバーは自己署名されたルート証明書を使用しており、パスワードで保護されたクライアント証明書の提示を要求しています。サーバーのルート証明書とクライアントの証明書を、デフォルトの Java 鍵ストアに追加しました。 /System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home/lib/security/cacerts (OSX 10.5)に保存しています。 鍵ストア ファイルの名前は、クライアント証明書がそこに入ることになっていないことを示唆しているように思えますが?

とにかく、このストアにルート証明書を追加することで、悪名高き javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed' problem.

しかし、クライアント証明書をどのように使用するかで行き詰っています。2 つのアプローチを試しましたが、どちらも埒があきません。

最初に、そして望ましいのは、試してみることです。

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://somehost.dk:3049");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
// The last line fails, and gives:
// javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

HttpsURLConnectionクラスをスキップして(サーバーとHTTPで話したいので理想的ではありません)、代わりにこうしてみました。

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
SSLSocket sslsocket = (SSLSocket) sslsocketfactory.createSocket("somehost.dk", 3049);
InputStream inputstream = sslsocket.getInputStream();
// do anything with the inputstream results in:
// java.net.SocketTimeoutException: Read timed out

クライアント証明書がここでの問題であるかどうかもわかりません。

どのように解決するのですか?

やっと解けました(^^)。強いヒントを得た はこちら (Gandalfsの回答もそれに少し触れています)。ミッシングリンクは、(ほとんど)以下のパラメーターの最初の部分と、キーストアとトラストストアの違いを見落としていた部分です。

自己署名されたサーバー証明書は、トラストストアにインポートされる必要があります。

keytool -import -alias gridserver -file gridserver.crt -storepass $PASS -keystore gridserver.keystore

これらのプロパティは、(コマンドラインまたはコードで)設定する必要があります。

-Djavax.net.ssl.keyStoreType=pkcs12
-Djavax.net.ssl.trustStoreType=jks
-Djavax.net.ssl.keyStore=clientcertificate.p12
-Djavax.net.ssl.trustStore=gridserver.keystore
-Djavax.net.debug=ssl # very verbose debug
-Djavax.net.ssl.keyStorePassword=$PASS
-Djavax.net.ssl.trustStorePassword=$PASS

動作例のコードです。

SSLSocketFactory sslsocketfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
URL url = new URL("https://gridserver:3049/cgi-bin/ls.py");
HttpsURLConnection conn = (HttpsURLConnection)url.openConnection();
conn.setSSLSocketFactory(sslsocketfactory);
InputStream inputstream = conn.getInputStream();
InputStreamReader inputstreamreader = new InputStreamReader(inputstream);
BufferedReader bufferedreader = new BufferedReader(inputstreamreader);

String string = null;
while ((string = bufferedreader.readLine()) != null) {
    System.out.println("Received " + string);
}