2way ssl with java - java

I am trying at write a java program that hits a url over ssl, and prints out the response to find out if the application on this port is running or not. We are using 2way ssl. I am fairly new to working with ssl and java security. Right now I am getting this error
Remote host closed connection during handshake
I am using this command to run the program
java -Djavax.net.ssl.trustStore=rs.truststore TmpUtil
Is there a way to find out what am I doing wrong and where exactly is the problem ?

You can generally debug an SSL/TLS connection that uses the JSSE in Java using the javax.net.debug system property. You'll find more details in the documentation.
Since you're after client-certificate authentication, it's most likely that your application needs a keystore to be configured. You'll find some details about the difference between keystore and truststore in this answer, and in the JSSE Reference Guide of course.

Related

Java seems to accept certificate with ANY CN [duplicate]

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.

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 mail TLS authentcation

I am trying to get a grasp on the fundamentals of Java Mail API and TLS. I have the following scenario:
There is an STMP server that uses TLS & SSL. If I log on to this server with some client, I can send authenticated &verified e-mails without any problems.
Then I try to run a web server on a different machine, that sends mail using the previously mentioned SMTP server. I still want to send TLS & SSL emails, however no matter how I configure the startup properties I get the following well known error:
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
I found a lot of people having similar issues, however my question is this:
Considering the previuosly described scenario, do I have to get some kind of certificate to the web server (possible somewhere in the JRE), or should it just work fine since the mail server already has that certificate & authentication mechanizm running. Shouldn't it be possible to just use the certificate of the SMTP server? Anyway, if I have to install the certificate to the machine that uses the STMP server how can I get that certificate?
I'm pretty new to JavaMail API and I have seen lots of articles about this but I could not find the answer black & white for my question.
Your client (that is in your case the one running on the webserver) needs to verify the SSL certificate of the mail server. It seems that your java truststore doesn't contain that certificate.
So you either need to put that certificate into the default truststore of your JRE (what I wouldn't recommend) or define a different truststore for your application (that of course needs to contain the mail servers certificate). To do that set this VM parameter: Djavax.net.ssl.trustStore=<path-to-truststore>
Edit: Ah I missed some part of your question.
To get the certificate of the mail server use something like openssl. See for example:
https://serverfault.com/questions/139728/how-to-download-ssl-certificate-from-a-website
The answer is in the JavaMail FAQ.
Quoted text from the linked site:
Q: When connecting to my mail server over SSL I get an exception like "unable to find valid certification path to requested target".
A: Your server is probably using a test certificate or self-signed certificate instead of a certificate signed by a commercial Certificate Authority. You'll need to install the server's certificate into your trust store. The InstallCert program will help.
Alternatively, you can set the "mail.protocol.ssl.trust" property to the host name of your mail server. See the javadocs for the protocol provider packages for details.
Other common causes of this problem are:
There's a firewall or anti-virus program intercepting your request.
There's something wrong in your JDK installation preventing it from finding the certificates for the trusted certificate authorities.
You're running in an application server that has overridden the JDK's list of trusted certificate authorities.

Client Certificate using Weblogic

Background:
I am working on a project that involves the client to authenticate itself (via client certificate). We are trying to launch a third party app which first authenticates the client cert and then launches its app.
We are using Java and SSL hanshake, and our client server is Weblogic 10.x. The client certificate is in PKCS12 format (cannot change this)
This is the piece of code for presenting the certificate:
System.setProperty("javax.net.ssl.keyStore","MyPath/cert.pfx");
System.setProperty("javax.net.ssl.keyStorePassword","MyPwd");
System.setProperty("javax.net.ssl.keyStoreType","PKCS12");
url.openConnection();
Now all this works pretty well from a standalone. The problem starts when we put this on weblogic. Sometimes it works, sometimes 'Client certificate not present' (http 403.7) is returned. I have tried various combinations (including configuring custome keystore on weblogic console) but nothing seems to work. Any idea why would weblogic altogether ignore the SSL settings (it doesnt throw an error even if I set keystore to a non-existent file)? System.setProperty for SSL just appears useless on weblogic!
In Weblogic 12c you also need add the parameter -DUseSunHttpHandler=true in order to tell the weblogic server to use the Sun Http Handlers instead of its own. Therefore you need the following Java VM parameters:
-Djavax.net.ssl.keyStore=MyPath/cert.pfx
-Djavax.net.ssl.keyStoreType=PKCS12
-Djavax.net.ssl.keyStorePassword=MyPwd
-DUseSunHttpHandler=true
Finally found the solution! Weblogic seems to over-ride the keystore configurations if it is done through Java as above. The thing that worked for me was to configure the SSL settings on JVM level, at web-logic start-up.
To make it work, I just added these Java options in the start-script for weblogic:
-Djavax.net.ssl.keyStore=MyPath/cert.pfx
-Djavax.net.ssl.keyStoreType=MyPwd
-Djavax.net.ssl.keyStorePassword=PKCS12
Restarted web-logic and SSL has been working great since then :)

What would cause SSL negotiations to succeed under .NET but fail under Java?

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.

Categories