ActiveMQ SSLException with Java 1.8.0_271-b09 client - java

I'm running an ActiveMQ server with SSL authorization (via trust store).
The clients are written with Spring Boot and Camel. Each client has it's individual certificate.
When the client's Java version is updated to version 1.8_271 the SSL connection suddenly fails. This can be found in the ActiveMQ logs:
javax.net.ssl.SSLException: Received fatal alert: unexpected_message
After downgrading to 1.8.0_261 everything is back to normal.
And here is where it gets really weird: my ActiveMQ truststore currently contains 232 certificates. When I delete 2 of them (does not matter which one) the connection with the 1.8_271 client works again.
This really does not make any sense to me. How can the number of items in the server's truststore have anything to to with the client's Java version?
Some updates:
I'm testing with the ActiveMQ Docker image
Changing the key store type from native JKS to PKCS #12 does not make a difference
Using Java 1.8_271 on the server side behaves the same but shows a different error message:
java.net.SocketException: Connection or outbound has closed

From the release notes
Improve Certificate Chain Handling
A new system property, jdk.tls.maxHandshakeMessageSize, has been added to set the maximum allowed size for the handshake message in TLS/DTLS handshaking. The default value of the system property is 32768 (32 kilobytes).
If your server requests client authentication, JSSE (edit) below 1.3 sends a CertificateRequest message specifying acceptable CA names derived from the certificates in your truststore, thus the number of certificates in your truststore affects the size of this message and may make it exceed the limit in which case the client rejects it (although I'm not sure I like using unexpected_message for this case).

Related

Successful handshake despite bad certificate in Netty 4.1.36 and jdk 11

I have client and server configured to use TLS and self-signed certificate.
Client SSL Engine is configured to use dummy trust manager, which doesn't ever throw CertificateException and empty KeyManager array.
Server SSL Engine uses key store which is initialized with manually generated key store file.
When I run it with JDK 8 I get the following handshake result:
Server fails to validate certificate
In client's thread I can see that io.netty.handler.ssl.SslHandler#setHandshakeFailure is called
and io.netty.handler.ssl.SslHandler#setHandshakeSuccess is never
called.
Which is expected behavior.
When I run it with JDK 11 I get the following:
server fails with the same error (empty certificate chain) but in client thread I see the following:
io.netty.handler.ssl.SslHandler#setHandshakeSuccess is called first
io.netty.handler.ssl.SslHandler#setHandshakeFailure is called after
I'm new to TLS 1.3 and might missed something in configuration. At the same time documentation says there's nothing to update for java TLS API clients to switch to TLS 1.3.
This behavior is confusing and it breaks further logic based on handshakePromise.
Full code to reproduce the issue is available by gist link:
https://gist.github.com/kiturutin/ccb710f67ccfb0a7a7de1fb3b3099b60
It's a groovy script and it first starts server then client.
To be clear, your server requests client auth and server validation of the client cert fails because the client is configured with no keymanager and thus sends no cert? (Whereas server sent its own 'manual' cert okay, and client accepted it because of the dummy trustmanager.)
If so this looks like a similar problem jetty has(?) -- see the fifth post in https://github.com/eclipse/jetty.project/issues/2711 -- and it appears to me this is because TLS 1.3 client considers the handshake complete on sending Finished (because 1.3 moves server Finished to first flight) and that's before receiving the server alert for client auth failure. (Whereas in 1.2 and earlier, server Finished and possibly Ticket basically is the second flight, causing an extra RTT for client-first app protocols -- like HTTP.)
It is also before receiving server NewSessionTicket (as modified for 1.3 'resumption') if used; see OpenJDK 11 problem - Client finished handshake before last UNWRAP .
I guess for 1.3 you have to accept 'failure' after 'success', unless the Java folks can think of some really clever fix for this.

SMTP TLS certificate

I'm having some problems understanding how TLS/SSL is working for email.
I have some questions.
In my development machine if I debug the following code fails the first time arround on the "sslSocket.startHandshake()" line, but if I try it again straight away it is working fine.
The error message that I'm getting is: "Remote host closed connection during handshake".
When I deploy the same code to our staging environment and send an email the code is working fine first time.
Both the development and staging server are in the same network and both have no anti virus programs runnning.
The only thing that I can think of as to why it is not working the first time around in the development environment is because I'm stepping through the code with the debugger and it's slower because of this.
Do you have any knowledge as to why I am receiving this error?
The code underneath is creating an SSL Socket. I'm curious to know if this code is enough for the connection with the mail server to be secure. Are these SSLSocketFactory classes dealing with certificates themselves?
2a) Or do I still need to specify a certificate somehow?
2b) Or is this code getting the certificate from the server and using the certificate to encrypt the data and send the encrypted data back and forth to the email server?
I know that it should work like it is described here:
RFC 3207 defines how SMTP connections can make use of encryption. Once a connection is established, the client issues a STARTTLS command. If the server accepts this, the client and the server negotiate an encryption mechanism. If the negotiation succeeds, the data that subsequently passes between them is encrypted.
2c) Is the code underneath doing this?
socket.setKeepAlive(true);
SSLSocket sslSocket = (SSLSocket) ((SSLSocketFactory) SSLSocketFactory.getDefault()).createSocket(
socket,
socket.getInetAddress().getHostAddress(),
socket.getPort(),
true);
sslSocket.setUseClientMode(true);
sslSocket.setEnableSessionCreation(true);
sslSocket.setEnabledProtocols(new String[]{"SSLv3", "TLSv1"});
sslSocket.setKeepAlive(true);
// Force handshake. This can throw!
sslSocket.startHandshake();
socket = sslSocket;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
In my development machine if I debug the following code fails the first time arround on the "sslSocket.startHandshake()" line, but if I try it again straight away it is working fine.
The error message that I'm getting is: "Remote host closed connection during handshake". []
The only thing that I can think of as to why it is not working the first time around in the development environment is because I'm stepping through the code with the debugger and it's slower because of this.
If you just do startHandshake() again with the underlying socket closed it should never work. If you go back to doing the TCP connection (e.g. new Socket(host,port)) and the initial SMTP exchange and STARTTLS, then yes I would expect it to avoid whatever problem affected the previous connection.
Yes, the server timing out because of the delay while you were debugging is quite possible, but to be certain you need to check logs on the server(s).
The code underneath is creating an SSL Socket. I'm curious to know if this code is enough for the connection with the mail server to be secure. Are these SSLSocketFactory classes dealing with certificates themselves?
Indirectly, yes. SSLSocketFactory creates an SSLSocket linked to an SSLContext which includes a TrustManager which is normally loaded from a truststore file. Your code defaults to the default SSLContext which has a TrustManager loaded from the default truststore, which is the file jssecacerts if present and otherwise cacerts in the lib/security directory in the JRE you are running. If your JRE hasn't been modified (by you or anyone else authorized on your system), depending on your variant or packaging of Java the installed JRE usually has no jssecacerts and contains or links to a cacerts file that (initially) contains root certs for about a hundred 'well-known' or established certificate authorities like Symantec, GoDaddy, Comodo, etc.
2a) Or do I still need to specify a certificate somehow?
Since when the handshake is done it is successful, obviously not.
2b) Or is this code getting the certificate from the server and using the certificate to encrypt the data and send the encrypted data back and forth to the email server?
Kind of/sort of/not quite. With some exceptions not applicable here, in an SSL/TLS handshake the server always provides its own certificate and usually intermediate or 'chain' certificates that link its cert to a trusted root cert (such as the abovementioned Symantec etc). The server cert is always used to authenticate the server, and sometimes alone but often combined with other mechanisms (particularly Diffie-Hellman ephemeral DHE or its elliptic-curve variant ECDHE) used to establish a set of several symmetric key values which are then used to encrypt and authenticate the data in both directions. For a more complete explanation see the canonical question and (multi-part!) answer in security.SX https://security.stackexchange.com/questions/20803/how-does-ssl-work/
2c) Is the code underneath doing this?
It is starting an SSLv3 or TLSv1 client-side session on an existing socket. I'm not sure what other question you have here.
You might be better off leaving out the setEnabledProtocols(). Sun/Oracle Java version 8, which is the only one now supported, supports TLS 1.0, 1.1 and 1.2 by default. 1.1 and especially 1.2 are definitely better than 1.0, and should definitely be offered so that if the server supports them they get used. (Sun/Oracle 7 is more problematic; it implements 1.1 and 1.2, but does not enable them client side by default. There I would look at .getSupportedProtocols and if 1.1 and 1.2 are supported but not enabled I would add enable them. But if possible I would just upgrade to 8. Other versions of Java, notably IBM, differ significantly in crypto details.)
SSLv3 should not be offered unless absolutely necessary; it is now badly broken by POODLE (search on security.SX for dozens of Qs about POODLE). I would try without it, and only if the server insists on it re-enable it temporarily, _along with TLS 1.0 through 1.2 whenever possible, and simultaneously urge the server to upgrade so I can remove it again.

java 1.6 TLS1.2 support using proxy nginx/ squid solution issues

I have a legacy java web application that makes calls to an external webservice. The provider of that service is turning off TLS1.0 support. So, I am trying to see how the application can continue to work with the service.
The options I have seen are a) use BouncyCastle JCE instead of Java JCE http://boredwookie.net/index.php/blog/how-to-use-bouncy-castle-lightweight-api-s-tlsclient/, which I guess requires code change/ recompile (we don't have the luxury of doing it) or 2) use proxy servers https://www.reddit.com/r/sysadmin/comments/48gzbi/proxy_solution_to_bump_tls_10_connection_to_tls_12/
I have tried nginx proxy - it doesn't seem to handle the switch between TLS1.0 incoming and TLS1.2 that the end server expects.
server { listen 443 ssl; server_name proxy.mydomain.com;
ssl_certificate D:/apps/openssl/proxy.mydomain.com.cert;
ssl_certificate_key D:/apps/openssl/proxy.mydomain.com.private;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP;
ssl_prefer_server_ciphers on;
location / {
proxy_pass https://fancyssl.hboeck.de/;
}
This fails with a 502/ bad gateway error since https://fancyssl.hboeck.de only support TLS1.2 but works with https://www.google.com that supports TLS1.0.
I am doing this on Windows.
It's not TLSv1.2, it's lack of SNI leading to renegotiation.
First, I set up nginx (1.8.1/Windows) with a config like yours except using my own key&cert and proxying to my own test server. It worked fine, connecting from Java6 requester with TLSv1.0 and to server with TLSv1.2 (and even ECDHE-RSA-AES256GCM-SHA384, one of the 'best' ciphersuites) and returned pages just fine. I tried fancyssl.hboek.de and got 502 like you.
With wireshark I saw that nginx does not send SNI (by default) and at least using the IPv4 address 46.4.40.249 (I don't have IPv6) that server apparently hosts more than one domain because without SNI it provides a different (and expired!) certificate, for *.schokokeks.org, and after the first application data (the request) it sends an encrypted handshake (a renegotiation request -- which nginx does not honor). Testing with openssl s_client confirms that the server with SNI immediately sends the page but without it renegotiates first; repointing nginx to openssl s_server confirms that if the server requests renegotiation, receives no response, and closes nginx treats that as 502.
I would guess that Apache is renegotiating because it realizes the requested Host is not covered by the certificate -- except that it again uses the 'wrong' certificate. I haven't tried to track that part down.
Google does support TLSv1.2 (and ECDHE-RSA-AESGCM) when I connect, but even without SNI doesn't renegotiate, presumably because it's such high volume nothing else runs on www.google.com servers and there's no ambiguity. My test server doesn't have vhosts so didn't need SNI.
The nginx documentation reveals a directive proxy_ssl_server_name which can be set on to enable SNI, and then proxying to this server works.
FYI: several of the statements on that webpage are wrong, although its conclusion (if possible use TLSv1.2 with ECDHE or DHE and AES-GCM) is good.
Also, most of your ssl_ciphers string is useless, but you didn't ask about that.
ssl_ciphers HIGH:SEED:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!RSAPSK:!aDH:!aECDH:!EDH-DSS-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!SRP
HIGH is an excellent start.
SEED is useless in a server used (only) by Java/JSSE client, because it's not implemented on the Java side. Even outside of Java it was pretty much used only in South Korea, where it was created as an alternative to DES or IDEA, and even there it is mostly obsoleted by ARIA which is an alternative to AES -- but is not implemented by OpenSSL and hence nginx.
aNULL is probably unneeded because JSSE disables 'anonymous' suites by default, but here it's worth it as defense in depth.
!eNULL does nothing; no eNULL suites are in HIGH, or DEFAULT, or even ALL. You can only get them explicitly or with the bizarre COMPLEMENTOFALL -- which you shouldn't.
!EXPORT !DES !RC4 do nothing; none of them are in HIGH. If instead you started from DEFAULT on older versions of OpenSSL, or from ALL, then they would be good.
!PSK is unneeded; nginx doesn't appear to configure for PSK and JSSE doesn't implement it anyway.
!RSAPSK is ignored because OpenSSL doesn't implement that keyexchange, and if it did those suites are already covered as above.
!aDH !aECDH are covered by !aNULL and thus do nothing.
!EDH-DSS-DES-CBC3-SHA is silly; there's no reason to exclude this one suite when you keep other DHE_DSS and 3DES suites.
!KRB5-DES-CBC3-SHA is ignored because OpenSSL doesn't implement Kerberos, and if it did nginx wouldn't be configured for it plus again it would be silly to exclude one suite while keeping similars.
!SRP is unneeded; like PSK nginx apparently doesn't configure and JSSE doesn't implement.
So: HIGH:!aNULL is all you need.

Google Authentication errors when accessing token endpoint from JVM

We run a web application with a JVM backend (Java 7 update 75; code is in Scala, but I don't believe this is relevant). We use Google for authentication via Oauth.
There have been a handful of days over the last couple of months on which we have been intermittently unable to authenticate users.
The redirect to and from Google is successful, but when we try to call the token_endpoint at https://www.googleapis.com/oauth2/v4/token to validate the authentication we sometimes get the following exception: javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation.
This comment on another question led me to find a JDK bug that can manifest as this exception (What means "javax.net.ssl.SSLHandshakeException: server certificate change is restrictedduring renegotiation" and how to prevent it?).
My working hypothesis is:
The bug (https://bugs.openjdk.java.net/browse/JDK-8072385) means that only the first entry in the Subject Alternative Name (SAN) list is checked. The exception above is thrown when the hostname being verified is in the SAN list, but not at the top of the list.
Yesterday (27th May 2015 from ) we saw two different certificates being intermittently served from www.googleapis.com. The first (serial 67:1a:d6:10:cd:1a:06:cc) had an SAN list of DNS:*.googleapis.com, DNS:*.clients6.google.com, DNS:*.cloudendpointsapis.com, DNS:cloudendpointsapis.com, DNS:googleapis.com whilst the second (serial 61:db:c8:52:b4:77:cf:78) had a SAN list of: DNS:*.storage.googleapis.com, DNS:*.commondatastorage.googleapis.com, DNS:*.googleapis.com.
Due to the bug in the JVM, we can validate the first certificate, but the exception is thrown with the second (despite being perfectly valid) as *.googleapis.com is not the first entry in the SAN list.
The fix is in the yet to be released 7u85 (no mention of when this will be available).
I've downgraded a single node of our cluster to 7u65, but the certificate seemed to be reverted at around the time we did this (last error we saw was 22:20GMT) so it's hard to pin down an affirmative fix.
Has anyone else experienced this or something similar and have any other workaround other than downgrading (downgrading removes various other SSL/TLS checks)?
I am not really sure that your problem is related to a JVM bug.
There is a fix in Java 6 and above for CVE-2014-6457, "Triple Handshake attack against TLS/SSL connections (JSSE, 8037066)", prevents peer certificates changing during renegotiation.
Problem explanation:
A security vulnerability in all versions of the Transport Layer
Security (TLS) protocol (including the older Secure Socket Layer
(SSLv3)) can allow Man-In-The-Middle (MITM) type attacks where chosen
plain text is injected as a prefix to a TLS connection. This
vulnerability does not allow an attacker to decrypt or modify the
intercepted network communication once the client and server have
successfully negotiated a session between themselves.
However, if the potentially changed certificate is for the same identity as the last seen certificate then the connection is allowed.
Two identities are considered equal in this case:
There is a subject alternative name specified in both certificates which is an IP address and the IP address in both certificates is the same.
There is a subject alternative name specified in both certificates which is a DNS name and the DNS name in both certificates is the same.
The subject and issuer fields are present in both certificates and contain identical subject and issuer values.
In other conditions (the identity of the certificate has changed) then a javax.net.ssl.SSLHandshakeException: server certificate change is restricted during renegotiation exception is raised.
Workaround:
Disable renegotiation (not recommended) applying the following JVM argument: -Djdk.tls.allowUnsafeServerCertChange=true it disables the unsafe server certificate protection.
Disable SSLv3 in outgoing HTTPS connections, Java 7 supports TLSv1.1 and TLSv1.2 in client mode but defaults to using TLSv1 in the TLS handshake. We should use TLSv1.1 and TLSv1.2 in client mode TLS in java 7 as well. Java 8 enables TLSv1.1 and TLSv1.2 in client mode(in addition to SSLv3 and TLSv1) and uses TLSv1.2 by default in TLS handshake. If you are creating the connection programatically and setting a socket factory use TLS instead of SSL.
Anyway, update your post with your google oauth client code before calling the token_endpoint to validate the authentication to see what might be happening.

Opening SSL TCP socket and sending web requests

I have a requirement to open a tcp socket and authenticate using SSLv3 or TLSv1 IP protocol using X.509 digital certificate.
What does this handshake process involve exactly? I know the each message should be encrypted and signed with my private key. What else?
After successful I've to send POST HTTP requests over the socket.
The server may decide to close this socket if inactive after some time. I need to be able to re-open, authenticate and send requests again.
The certificate given to me is in PKCS12 format with the following information.
Certificate Identification
,Certificate Public Key
,Certificate Private Key
,Certification Authority Chain
I'm fairly new to SSL can someone please provide pointers to how to go about implementing this in java or spring integration.
A good start is to see the javax.net.ssl.HttpsURLConnection javadocs: http://download.oracle.com/docs/cd/E17476_01/javase/1.4.2/docs/api/javax/net/ssl/HttpsURLConnection.html
Also you gonna need to use the keytool command to import the certificate into a keystore.
You don't need to know about the handshake, it is all done for you. Read the JSSE Reference as suggested to see what you +do+ have to worry about.
Regarding TLS/SSL details, for client-certificate authentication, compared with the "normal" hanshake, the server sends an extra CertificateRequest TLS message to the client, which responds with its certificate in a subsequent Certificate TLS message (later on, the client sends a CertificateVerify TLS message where it signs the other messages with its private key, so as to prove to the server that it does indeed have the private key for the public key in the certificate it sent.) Note that, once the handshake has finished, the messages are not encrypted with your private key, but with ephemeral keys shared with the server (agreeing on those keys confidentially is part of the handshake too).
In practice, you need a certificate and its private key, contained in the PKCS#12 file (for example) and to configure the client to send it when connecting to the server (the server will ask for it according to its configuration).
It's easier to assume you'll only need one certificate and won't have to make a choice between a number of certificates, otherwise, you need to set up your own X509TrustManager within the SSLContext.
If all your connections are likely to use this certificate, you may use the default settings, which HttpsURLConnection (and the default SSLSocketFactory) will pick up.
This can be done by:
setting the javax.net.ssl.keyStore, javax.net.ssl.keyStoreType and javax.net.ssl.keyStorePassword system properties on the command line with your settings. I would recommend against that because someone else on the machine could potentially see the command line and your settings by listing the processes (depending on the configuration of the machine),
setting those system properties within your application,
initialising an SSLContext and setting it as the default one via SSLContext.setDefault(..) (Java 6).
Note that .p12 (PKCS#12) files are a supported keystore out of the box, so you don't need to do any conversion with keytool, just use PKCS12 as the store type.
If you need these settings or, you may initialise an SSLContext, create an SSLSocketFactory from it and then configure the instance of HttpsURLConnection (if that's what you're using) with setSSLSocketFactory.
(You may be able to use tools like jSSLutils to help build the SSLContext.)

Categories