突如、WebMoneyのAPIが、SSLPeerUnverifiedExceptionを投げ始めた。
TLS1.2に移行したようです。
すでに知られていることですが、java7は、デフォルトではTLS1.2をサポートしていません。 TLS1.2限定のサーバにリクエストを送信すると、以下のようなExceptionがスローされました。
javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:421) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:128) at org.apache.http.conn.ssl.SSLSocketFactory.connectSocket(SSLSocketFactory.java:397) at org.apache.http.impl.conn.DefaultClientConnectionOperator.openConnection(DefaultClientConnectionOperator.java:148) at org.apache.http.impl.conn.AbstractPoolEntry.open(AbstractPoolEntry.java:149) at org.apache.http.impl.conn.AbstractPooledConnAdapter.open(AbstractPooledConnAdapter.java:121) at org.apache.http.impl.client.DefaultRequestDirector.tryConnect(DefaultRequestDirector.java:573) at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:425) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754) at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732)
近年のTSL1.2必須化の動きは、有名な話だけど、なぜか、本件は、アナウンスが届かなかった。 何も情報がなかったので、サーバ証明書の問題かなと思ってしまいました。
TLS1.2に、対応するには、いくつか方法があります。
- デフォルトで1.2をサポートしているから、java8以上にする。理想を言えばそうしよう。
- JVMの引数に、
-Djdk.tls.client.protocols=TLSv1.1,TLSv1.2,,TLSv1.2
や-Dhttps.protocols=TLSv1.1,TLSv1.2,TLSv1.3
をつけて実行しよう。 - これで駄目なケースもあるようだ。プログラムの修正が必要。
私の場合、3のパターンで。 Apache CommonsのHttpClientを使用しているので、以下のような修正をしました。
SSLContext sslContext = SSLContext.getInstance("TLSv1.2"); sslContext.init(null, null, new SecureRandom()); SSLSocketFactory sf = new SSLSocketFactory(sslContext); Scheme httpsScheme = new Scheme("https", 443, sf); SchemeRegistry schemeRegistry = new SchemeRegistry(); schemeRegistry.register(httpsScheme); ClientConnectionManager cm = new SingleClientConnManager(schemeRegistry); DefaultHttpClient client = new DefaultHttpClient(cm);
HttpClientのバージョンよっては、こちらの修正方法になります。 というかググるとこればかり出てきます。
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( SSLContexts.createDefault(), new String[] { "TLSv1.2", "TLSv1.3" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
よく見かけるこのやり方は、おそらく全体的に適用されるので、気をつける必要があると思う。
SSLContext ctx = SSLContext.getInstance("TLSv1.2"); ctx.init(null, null, null); SSLContext.setDefault(ctx);