Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SSLSocket ignores domain mismatch

Tags:

java

ssl

I am reading from SSL Socket but host doesn't match the certificate (eg. host = "localhost"). I would expect the exception but the following code happily talks to remote server without any problems.

try (
    final Socket socket = SSLSocketFactory.getDefault().createSocket(host, port);
    final OutputStream os = socket.getOutputStream();
    final InputStream is = socket.getInputStream()) {

    os.write(("HEAD / HTTP/1.1\r\nHost: " + host + "\r\nConnection: close\r\n\r\n").getBytes());
    os.flush();

    final byte[] bytes = new byte[1024];
    int n;
    while ((n = is.read(bytes)) != -1) {
        System.out.print(new String(bytes, 0, n));
    }
    System.out.println();
} catch (final IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Therefore I've tried another approach:

try {
    final HttpURLConnection conn = (HttpURLConnection) new URL("https://" + host + ":" + port + "/").openConnection();

    try (InputStream is = conn.getInputStream()) {
        IOUtils.copy(is, System.out);
    } catch (final IOException e1) {
        try (InputStream es = conn.getErrorStream()) {
            if (es != null) {
                IOUtils.copy(es, System.out);
            }
        }
    }
} catch (final IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Unfortunately I still get no SSL exception, just WARN in the logs: 2013-07-31 16:02:27,182 WARN nio - javax.net.ssl.SSLException: Received fatal alert: certificate_unknown

How to get the SSL exception if certificate doesn't match?

like image 397
Martin Ždila Avatar asked Jul 31 '13 14:07

Martin Ždila


2 Answers

The SSL/TLS protocol specification is modular and detached from the specifications used to authenticate the remote host. These other specifications are split into two categories: verifying that the certificate itself can be trusted (RFC 3280/5280) and verifying the identity in the certificate (RFC 6125, or RFC 2818 for HTTPS).

The JSSE integrates the SSL protocol and the verification of the certificate in the SSLSocket (or SSLEngine) API, but doesn't handle the verification of the identifier (whch is equally important).

This is mainly due to the fact that the SSLSocket/SSLEngine can apply to any application protocol (e.g. HTTP, IMAP, SMTP, LDAP, ...), but the rules for verifying the identifier were in different specifications (with small variations), until RFC 6125 (which is still quite recent).

HttpsURLConnection handles both, because it also uses a HostnameVerifier, which follows the HTTPS specification (RFC 2818, Section 3.1). This is done separately from the SSLSocket/SSLEngine API. For other protocols, you may need to implement what the protocol specification says.

This being said, since Java 7, there is a mechanism to verify the identity of the certificate directly as part of the SSLSocket/SSLEngine API.

SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm("HTTPS");
sslSocket.setSSLParameters(sslParams);

Using this should make it throw an exception if the host name doesn't match.

There aren't major differences between HTTPS and the more uniform specifications in RFC 6125 (besides the fact that the latter considers IP addresses out of scope). Even if you're not using HTTPS, it would still generally make sense to use its identification specifications for other protocols. (Perhaps an "RFC 6125" endpoint identification algorithm might come in later versions of Java.)

like image 188
Bruno Avatar answered Oct 18 '22 01:10

Bruno


Hostname matching isn't part of SSL, it is part of HTTPS. See for example javax.net.ssl.HostnameVerifier.

The log you quote indicates that an SSLException was thrown by HttpsURLConnection.

like image 20
user207421 Avatar answered Oct 18 '22 00:10

user207421