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.
Related
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.
This question already has answers here:
Writing a SSL Checker using Java
(2 answers)
Closed 5 years ago.
This question is NOT a duplicate of question pointed to. There is NOWHERE in mentioned question anything about fact that TLS does not perform hostname verification by itself.
I have ActiveMQ instance and client in Java. Client uses JMSTemplate (org.springframework.jms.core.JmsTemplate) with factory org.apache.activemq.ActiveMQSslConnectionFactory. I have created self-signed certificates and with them trust store and keystore. Trust stores and keystores are read by both programs, I checked it by running both programs with
-Djavax.net.debug=all
Now my problem is that it seems that client absolutely ignores server hostname verification. Client connects to ActiveMQ using URL:
ssl://localhost:61616?jms.useCompression=true
Now, I tried to check whether everything will fail as expected if I change CN on ActiveMQ's certificate and well, it didn't went well. I changed to CN to e.g:
CN=google.com
or to:
CN=some.random.xxx333aaa.net.pp
but all these values seem to be OK with Java. Also note that there are no SANs (that is subjectAltNames). What more I tried to connect to ActiveMQ with such certificate but installed on different machine, and it seems that it all works well. Which is NOT what I want.
Also: I have finally uninstalled all Java versions and installed 1.8.0_144, using only JDK installer, installed jce_policy-8 in both places (it installs both JRE and JDK), did the same on remote machine too.
If you will examine RFC 2246 (TLS) and RFC 2818 (HTTPS) you will discover that hostname verification is part of HTTPS, not part of TLS. In TLS it is entirely up to the application to perform an authorization step.
So in fact my question is: how to force hostname verification?
See this answer.
Ok, I think I found an answer. Check this link:
https://issues.apache.org/jira/browse/AMQ-5443
and link mentioned in link above:
https://tersesystems.com/2014/03/23/fixing-hostname-verification/
It seems that TLS against what I thought DOES NOT PERFORM HOSTNAME VERIFICATION. This is absolutely stunning, but it seems that this is exactly the case. If no one will provide better answer I'll accept my own answer.
EDIT: Also see this:
https://docs.oracle.com/javase/8/docs/technotes/guides/security/jsse/JSSERefGuide.html
and look specifically at this part:
Cipher Suite Choice and Remote Entity Verification
The SSL/TLS protocols define a specific series of steps to ensure a protected connection. However, the choice of cipher suite directly affects the type of security that the connection enjoys. For example, if an anonymous cipher suite is selected, then the application has no way to verify the remote peer's identity. If a suite with no encryption is selected, then the privacy of the data cannot be protected. Additionally, the SSL/TLS protocols do not specify that the credentials received must match those that peer might be expected to send. If the connection were somehow redirected to a rogue peer, but the rogue's credentials were acceptable based on the current trust material, then the connection would be considered valid.
When using raw SSLSocket and SSLEngine classes, you should always check the peer's credentials before sending any data. The SSLSocket and SSLEngine classes do not automatically verify that the host name in a URL matches the host name in the peer's credentials. An application could be exploited with URL spoofing if the host name is not verified.
Protocols such as HTTPS (HTTP Over TLS) do require host name verification. Applications can use HostnameVerifier to override the default HTTPS host name rules. See HttpsURLConnection for more information.
I would like to know how does a regular SSL protocol differ from SSL pining. By setting up https, we are able to encrypt the request using SSL. Man in middle attack will not be able to see the raw payload. I also know SSL pinning is another way to prevent man in middle attack. But my question is if a proxy will always see the encrypted data only under https protocol, why do we still need to bundle certificate at client side and have SSL pinning? What advantage can SSL pining give us ?
Certificate pinning means the client has the server's certificate "built-in" and doesn't use your computer's trusted store. This means that even if your IT dept installs their own root cert, it won't be used.
A particularly clever IT department could install their root cert on your computer, use a proxy like Charles to create fake site certs on the fly, and re-write your downloaded program on the fly, replacing the pinned cert, but most aren't sophisticated enough to do that final step.
And you could probably just download the software from home too, in which case the pinned cert will be okay, and IT would never see the content on the transmissions.
Note: Same url is working successfully in browser, but it’s not working through java program same url.
Java code:
String urlString= "https://<host>:<port>/TestProject";
URL url = new URL(urlString);
HttpsURLConnection con = (HttpsURLConnection)url.openConnection();
InputStream ins = con.getInputStream();
InputStreamReader isr = new InputStreamReader(ins);
BufferedReader in = new BufferedReader(isr);
String inputLine;
while ((inputLine = in.readLine()) != null) System.out.println(inputLine);
in.close();
Exception:
Exception in thread "main" javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1720)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:954)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1138)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1165)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1149)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1172)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:234)
Please give the solutions as early as possible for this. Please give the possibilities to get this scenario if knows, it will very useful for me.
Finally i got solution:
Please look into http://myjavacafe.blogspot.in/2015/01/exception-received-fatal-alert.html
First: the server probably logged more detailed information about the problem, which could allow determining and fixing the actual problem. But since you want an early and "possible" answer rather than an accurate one, causes I've seen are:
Version too old: The server requires a newer protocol than your Java client offers. You appear to be running Java6, which only implements up to TLSv1.0. If the server demands higher, it will fail the handshake, although requiring above 1.0 is somewhat controversial for now (it likely will become accepted and common over the next several years). Solution: use Java8 (in its default configuration), or at least Java7 and override its default configuration which only offers up to TLSv1.0 for client.
Version too new: TLS specs (all) call for servers to negotiate a newer-version or otherwise more-capable client down to the server capabilities, but some servers apparently have bugs and instead fail the handshake. (Browsers/clients often handle this by falling back to older protocols, which caused the recent "POODLE" attack. See for example https://security.stackexchange.com/questions/71427/is-java-client-vulnerable-by-poodle and https://security.stackexchange.com/questions/70719/ssl3-poodle-vulnerability .) However, Java6 client is unlikely to trigger such a problem, and if a recent browser and especially several recent browsers can connect this is very unlikely. But if this is the problem Solution: use older Java (poor), or configure to use old protocol version(s) and possibly few(er) ciphers.
Extensions: Similarly it is possible a buggy server fails to skip unimplemented extensions as the specs say it should. Again if modern browsers can connect this is very unlikely. But if it does you can't control the extensions Java uses so Solution: none. (At least none in Java. You could use an external adapter such as stunnel, or an application-level proxy or relay.)
No shared cipher: The server does not support any of the ciphersuites your client offers. This is unlikely unless your JRE/JVM is configured badly. Java implements nearly all the defined ciphersuites, and by default enables all the ones that are not badly insecure, except that Java6 only enables Elliptic Curve Cryptography suites if an ECC crypto provider is installed which it is not by default. A server admin might reasonably want and prefer EC, but to require it today would be imprudent. If ECC is the problem Solution: use Java7 or 8 or add an EC provider to Java6. If your JRE/JVM is configured badly to use insecure ciphersuites that the server should not agree to Solution: don't do that.
SNI: The server may fail the handshake if it requires Server Name Indication, an extension commonly required nowadays for virtual hosting, but your client does not provide it with the right value. However, Java6 URLConnection(https) derives SNI automatically from the URL, the same way browsers do, so if the same URL works in a recent browser that is not the problem.
Client auth: The server may require client authentication using a certificate, often abbreviated as requiring (a) client cert, and fail if your client doesn't provide one, or perhaps not the correct one. Your browser(s) may be configured to supply the correct cert&key automatically, although if you use multiple browsers that don't share a keystore (as for example IE and Chrome on Windows do) that may be less likely. Solution: configure your JVM/JRE (or for limited scope a customized SSLSocketFactory) to use a keystore that contains the correct privateKeyEntry (key and cert or chain) or if a keystore is already configured put the correct privateKeyEntry in that keystore.
We have to create a web service client using Apache CXF in Java. The thing is I cannot seem to get the SSL session to properly engage. Either it fails altogether, the server fails to decipher what is sent to it once the application data is transmitted or I fail to read the responses from the server.
However when trying the same transaction using a simple soap test client built in .NET everything runs smoothly.
Server is using double authentication.
Everything is certificate based (x509) stored in the windows certificate store (windows-MY and windows-ROOT)
edit
yes, double authentication is indeed client AND server authentication.
Thus far using the bountyCastle provider instead of SunMSCAPI seems to get further but still cannot get the client authentication to work.
PLatform of client CXF 2.2.9, Sun JDK 1.6_21
server IIS 6 ASP.NET unfortunately is all I could gather, I have no control over the server and must use it as-is.
update
I am using a JKS keystore now but still am getting the problem. It seems the client is not sending his certificate to the server as part of the authentication process. As a result I get a 403.7 error from the server.
Funny thing is that I receive this error message as an HTML page that must first be decrypted before it is readable !
Presumably, by double authentication, you mean you're using client-certificate authentication in addition to server-certificate authentication (which is more common).
It would be useful to know which versions of the platforms are used on either side, and which patches have been applied.
It's possible that some of the problem come from the re-negotiation fix to CVE-2009-3555 (or lack of fix).
The problem is a flaw in the initial design of the re-negotiation in TLS, which is what was used to re-negotiate a client-certificate. There are two ways of getting a client-certificate: either the server asks for it during the initial TLS handshake, or it asks for it during a subsequent handshake (for example, once it has figured out what the request was aimed for and/or when trying to access a certain restricted area). The second method is the re-negotiation. Unfortunately, there was a security flaw in the design of the TLS protocol in that respect, which has since been fixed thanks to a TLS extension described in RFC 5746.
When the flaw was initially disclosed (around November 2009), some platforms and libraries such as Sun Java or OpenSSL rolled out a quick fix which simply disallowed any re-negotiation (so only initial negotiation of the client-certificate would work). Later on, once RFC 5746 was written, these libraries started to roll out implementations supporting this extension.
As far as I'm aware, Microsoft's default in IIS and its web framework was to use re-negotiation and not initial negotiation. In addition, it didn't roll out the initial fix to disable re-negotiation (effectively keeping the known vulnerability). It only rolled out a patch (still tolerant to old implementations by default) quite recently: Microsoft Security Bulletin MS10-049 - Critical.
There is also an explanation of the problem on this Microsoft security blog:
http://blogs.technet.com/b/srd/archive/2010/08/10/ms10-049-an-inside-look-at-cve-2009-3555-the-tls-renegotiation-vulnerability.aspx
Essentially, if you're trying to talk to a server that only supports the old negotiation style from a stack that only has the new re-negotiation style or no renegotiation at all, it's not going to work.
If your server is running using IIS or similar environment, you might be able to turn on initial client-certificate negotiation using netsh and its clientcertnegotiation=enable option.
Java doesn't rely on the OS certificate store and needs to use its own.
This will import your self-signed certificates.
cd JAVA_HOME/jre/lib/security
keytool -import -file server_cert.cer -keystore cacerts
I post this as an answer though I realize now the question was not formulated properly as I got thrown in a loop because the .NET example I had was actually performing a hack to get around the problem.
The proper question should have been
How to get Java to perform Client side Authentication on a server that does not ask for Ask for certificates ?
the answer is actually under our very noses, however to get to the answer one needs the correct question !!
Great thanks to Bruno who provided some very helpful information.
the solution can pretty much be summed up in these two questions :
Java HTTPS client certificate authentication
Client SSL authentication causing 403.7 error from IIS
Although the client is "not supposed" to send a certificate if not asked I found that by tweaking the client certificate in the keystore to contain the following :
Client certificate with all extensions
Client Private key
A concatenation of the client's complete certification chain.
push all this in the same certificate store and use it as keystore. Then load again the certification chain as a trust store. From there it should just work. This being said there is still a possibility for failure. the safest way to solve this particular issue is to have the server actively ask for a authentication certificate from the client by providing a list of accepted CA.
Hope this helps anyone else that can be stuck in the same problem, sure tooke me for a spin for a while before I reach the root of evil.