Java 11 RC - Handshake failure in certain specific cases throws a different exception than previous versions
Jaikiran Pai
jai.forums2013 at gmail.com
Tue Sep 18 02:06:50 UTC 2018
Hi Xuelei,
This isn't a significant issue in our case (there isn't any functional
difference in what was being done in that specific catch block, in the
code where this happened). I was only just curious if this was an
intentional. The SSLProtocolException which is now being thrown, I think
is a better one (having read its javadoc). I'll leave it at this now and
won't create a bug.
Thank you for your inputs.
-Jaikiran
On 18/09/18 7:29 AM, Xuelei Fan wrote:
> Hi Jaikiran,
>
> Normally, the thrown exception class can be an implementation choice,
> and may be not reliable from version to version. We were trying to
> use the same exception, but we may miss the use cases. I may suggest
> to make the code independent from it. However, if the impact is
> significant, please feel free file a bug and we will evaluate if there
> is something we can do.
>
> Thanks,
> Xuelei
>
> On 9/17/2018 6:30 PM, Jaikiran Pai wrote:
>> Just checking back on this one. Is this an expected change? Personally,
>> it's not a big issue in the code where this is happening for me. I'll
>> probably just change the catch block to a more generic IOException.
>> However, for any other code which relied on the previous SocketException
>> catch block, they will now have to expect a different exception
>> depending on what version of Java runtime it's running against.
>>
>> -Jaikiran
>>
>>
>> On 12/09/18 9:11 PM, Jaikiran Pai wrote:
>>> Please consider the code that's at the end of this mail. It is a simple
>>> client/server code where a HTTPS server is created and set to
>>> "needClientAuth". The client then uses HttpsURLConnection and (in this
>>> case intentionally) doesn't present any certificate, expecting the
>>> handshake to fail. In previous versions of Java, the handshake failure
>>> in this code would throw a java.net.SocketException as below:
>>>
>>> Exception in thread "main" java.net.SocketException: Connection reset
>>> at java.net.SocketInputStream.read(SocketInputStream.java:210)
>>> at java.net.SocketInputStream.read(SocketInputStream.java:141)
>>> at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
>>> at sun.security.ssl.InputRecord.read(InputRecord.java:503)
>>> at
>>> sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:983)
>>> at
>>> sun.security.ssl.SSLSocketImpl.waitForClose(SSLSocketImpl.java:1779)
>>> at
>>> sun.security.ssl.HandshakeOutStream.flush(HandshakeOutStream.java:124)
>>> at
>>> sun.security.ssl.Handshaker.sendChangeCipherSpec(Handshaker.java:1156)
>>> at
>>> sun.security.ssl.ClientHandshaker.sendChangeCipherAndFinish(ClientHandshaker.java:1266)
>>>
>>> at
>>> sun.security.ssl.ClientHandshaker.serverHelloDone(ClientHandshaker.java:1178)
>>>
>>> at
>>> sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:348)
>>>
>>> at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
>>> at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
>>> at
>>> sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
>>> at
>>> sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
>>>
>>> at
>>> sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
>>> at
>>> sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
>>> at
>>> sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
>>>
>>> at
>>> sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
>>>
>>> at
>>> sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564)
>>>
>>> at
>>> sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
>>>
>>> at
>>> sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:263)
>>>
>>> at ClientCertTest.main(ClientCertTest.java:26)
>>>
>>>
>>> However, in the Java 11 (release candidate) as well as Java 12
>>> (upstream), this code now throws a javax.net.ssl.SSLProtocolException
>>> with the java.net.SocketException wrapped in it:
>>>
>>> Exception in thread "main" javax.net.ssl.SSLProtocolException:
>>> Connection reset
>>> at
>>> java.base/sun.security.ssl.Alert.createSSLException(Alert.java:126)
>>> at
>>> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
>>>
>>> at
>>> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
>>>
>>> at
>>> java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
>>>
>>> at
>>> java.base/sun.security.ssl.SSLSocketImpl.handleException(SSLSocketImpl.java:1314)
>>>
>>> at
>>> java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:839)
>>>
>>> at
>>> java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
>>>
>>> at
>>> java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
>>>
>>> at
>>> java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
>>>
>>> at
>>> java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:746)
>>>
>>> at
>>> java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689)
>>> at
>>> java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:717)
>>> at
>>> java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1604)
>>>
>>> at
>>> java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
>>>
>>> at
>>> java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:245)
>>>
>>> at ClientCertTest.main(ClientCertTest.java:26)
>>> Caused by: java.net.SocketException: Connection reset
>>> at
>>> java.base/java.net.SocketInputStream.read(SocketInputStream.java:186)
>>> at
>>> java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
>>> at
>>> java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:448)
>>>
>>> at
>>> java.base/sun.security.ssl.SSLSocketInputRecord.bytesInCompletePacket(SSLSocketInputRecord.java:68)
>>>
>>> at
>>> java.base/sun.security.ssl.SSLSocketImpl.readApplicationRecord(SSLSocketImpl.java:1104)
>>>
>>> at
>>> java.base/sun.security.ssl.SSLSocketImpl$AppInputStream.read(SSLSocketImpl.java:823)
>>>
>>> ... 10 more
>>>
>>>
>>> Is this an intentional and expected change? As far as I could see,
>>> there
>>> isn't any specific API which says SocketException will be thrown in
>>> this
>>> case, so maybe the client applications which were catching this
>>> specific
>>> exception are expected to change their catch block to something more
>>> generic like a IOException?
>>>
>>> Here's the code to reproduce this:
>>>
>>> import java.io.*;
>>> import java.net.*;
>>> import javax.net.ssl.*;
>>> import java.util.*;
>>> import java.security.*;
>>> import com.sun.net.httpserver.*;
>>> import java.security.cert.*;
>>>
>>>
>>> public class ClientCertTest {
>>> private static final String keyFilename = "keystore";
>>> private static final String keystorePass = "passphrase";
>>>
>>> public static void main(final String[] args) throws Exception {
>>> final int port = 12345;
>>> final HttpsServer server = startServer("localhost", port);
>>> try {
>>> final URL targetURL = new URL("https://localhost:" +
>>> port +
>>> "/");
>>> final SSLContext sslctx = SSLContext.getInstance("TLS");
>>> sslctx.init(null, new TrustManager[] {new TrustAll()},
>>> null);
>>> HttpsURLConnection.setDefaultHostnameVerifier((h, s) ->
>>> {return true;});
>>> final HttpsURLConnection conn = (HttpsURLConnection)
>>> targetURL.openConnection();
>>> // setup the HTTPS connection to use our SocketFactory
>>> which
>>> doesn't present
>>> // any cert when asked for (and thus is expected to fail
>>> handshake)
>>> conn.setSSLSocketFactory(sslctx.getSocketFactory());
>>> try (final InputStream is = conn.getInputStream()) {
>>> is.read();
>>> } catch (SocketException se) {
>>> System.out.println("*** Received the expected
>>> SocketException: " + se.getMessage());
>>> throw se;
>>> }
>>> } finally {
>>> server.stop(0);
>>> System.out.println("Stopped server");
>>> }
>>> }
>>>
>>> private static HttpsServer startServer(final String host, final
>>> int
>>> port) throws Exception {
>>> final HttpsServer server = HttpsServer.create(new
>>> InetSocketAddress(host, port), 0);
>>> final Thread t = new Thread(() -> {
>>> try {
>>> final SSLContext sslctx =
>>> SSLContext.getInstance("TLS");
>>> final KeyManagerFactory kmf =
>>> KeyManagerFactory.getInstance("SunX509");
>>> final KeyStore ks = KeyStore.getInstance("JKS");
>>> try (final FileInputStream fis = new
>>> FileInputStream(keyFilename)) {
>>> ks.load(fis, keystorePass.toCharArray());
>>> }
>>> kmf.init(ks, keystorePass.toCharArray());
>>> sslctx.init(kmf.getKeyManagers(), null, null);
>>> final SSLParameters sslParameters =
>>> sslctx.getDefaultSSLParameters();
>>> // need client auth
>>> sslParameters.setNeedClientAuth(true);
>>> server.setHttpsConfigurator(new
>>> HttpsConfigurator(sslctx) {
>>> @Override
>>> public void configure(final HttpsParameters
>>> params) {
>>> params.setSSLParameters(sslParameters);
>>> }
>>> });
>>> server.start();
>>> System.out.println("Started server at " +
>>> server.getAddress());
>>> } catch(Exception e) {
>>> throw new RuntimeException(e);
>>> }
>>> });
>>> t.start();
>>> return server;
>>> }
>>>
>>> private static class TrustAll implements X509TrustManager {
>>>
>>> @Override
>>> public void checkClientTrusted(X509Certificate[] chain, String
>>> authType) throws CertificateException {
>>> return;
>>> }
>>>
>>> @Override
>>> public void checkServerTrusted(X509Certificate[] chain, String
>>> authType) throws CertificateException {
>>> return;
>>> }
>>>
>>> @Override
>>> public X509Certificate[] getAcceptedIssuers() {
>>> return new X509Certificate[0];
>>> }
>>> }
>>> }
>>>
>>>
>>> P.S: The "keystore" file in this example is the same one that's present
>>> in the openjdk repo at test/jdk/javax/net/ssl/etc/keystore, so in order
>>> to run the above code, that file can just be placed in the current
>>> working dir.
>>>
>>> -Jaikiran
>>>
>>
More information about the security-dev
mailing list