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?
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.)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With