Android HTTPS SSL reuse and caching - java

I know this has been asked several times. I've tried them all but none of them work as I want.
I'm polling a webservice repeatedly in 30 second intervals. The webservice is based on HTTPS / SSL. I want to remove the overhead of SSL handshake on each webservice call ( SSL reuse). How can I achieve this?
I've looked at SSLCertificateSocketFactory and SSLSessionCache which seems to be the answer. I can get an SSLSocket instance by calling
SSLSocketFactory sockFactory = SSLCertificateSocketFactory.getHttpSocketFactory(TIMEOUT, sslSession);
However I cannot find a way to set my keystore and password to this socket factory. I guess you can set the keystore and password only via an SSLSocketFactory constructor.
So my 2 problems are
1) Need SSL reuse
2) Need to use my keystore for accepting the certificates from the server.
Thanks

Related

SSL Configuration for Http Client

this question might sound I bit dummy but I have researched many questions/answers here and can't find the answer for my case.
Currently I am using RestTemplate library to make HTTP requests for my java library that I am currently working on. In order to have successful HTTP call to HTTPS URLs i needed to add a SSL configuration for my HTTP client. Something like this:
clientBuilder
.disableCookieManagement()
.setDefaultRequestConfig(requestConfig)
.setSSLSocketFactory(new SSLConnectionSocketFactory(SSLContexts.custom()
.loadTrustMaterial(null, new TrustSelfSignedStrategy())
.build()));
So this library is supposed to be shipped to the user as a jar executable application and I know that using self-signed SSL certificates is not a good idea for general usage since there might have web servers that do not acknowledge it. I read that If I get a signed SSL certificate then I should save the proper keys on Keystore and also use Trustore to acknowledge the server's certificate. I do not think I can just pass Keystore and Trustore to the client who executes my java library, so my question here is, does Java has a built-in SSL certificate, so I could somehow just do some configuration on HTTP client and the built-in certificates would be used. As far as I know, node.js offers something like that.
Could anyone just give me a explanation of how this works for java spring-boot?
There are two separate certificate verifications that could be happening. To connect to a server using https, you need to receive the server's certificate and validate it using a truststore.
In addition, it is possible for you to have a client certificate, and to pass that to the server so it can authenticate your client. Unless you have been told you need to do that, you don't. Unless the server has been specifically configured to do it, it isn't possible. If it is what you need to do, you need to obtain a client certificate, install it into a keystore and use that keystore in your client.
So for normal https, you do not need a keystore.
Whether you need "TrustSelfSignedStrategy" depends on the server.
If the server has a valid signed SSL certificate, you do not need to do anything special, RestTemplate will just work.
If the server has a self-signed certificate, you need to either configure the client to accept any self-signed certificate, or load the server's certificate into a truststore so the client knows to accept that specific certificate.

IS it possible to have One way and mutual ssl for same web App same time depending on URLs

I have a scenario where I have few rest web services, of which few need to enforce mutual ssl and few should just have one way ssl, here its same web application.
Is that possible in tomcat/Spring based application?
Sorry for replying late, yes I did this, not sure if the best way but kind of a hack.
Step 1: Have one way SSL set with clientAuth=want in your tomcat.
This will fix your scenario where you want to have one way ssl for all the webservices accept that one which needs extra/mutual authentication.
Step 2: Now for the web service which needs mutual ssl.
Write a servlet filter and for that particular web service url check the incoming http request for certificates. loop through the certs found in the request and match it with the certs from your trust store. if you found the match let the request flow proceed, if not return an exception as SSL cert not found.
X509Certificate[] certificates = (X509Certificate[]) request
.getAttribute("javax.servlet.request.X509Certificate");
The above code will give you array of cert in your request.
Note: Make sure your SSL configuration is correct or else the certificates variable stays null.
If you can use different hosts (assuming the client and server support SNI) or ports, then this should be no problem.
Unfortunately, you cannot vary the SSL configuration based on the URL's path since it is only available after the SSL connection has been established. Your only option in that case would be to make the client certificate optional and ignore any certificates sent for the URLs that do not require it.
In either case, you will almost certainly be better off letting something like Nginx or Apache httpd handle the SSL part and pass any data about the client's certificate (or lack thereof) to your Spring / Tomcat app in an HTTP header.
You can use TLS ("one-way") for your whole site and then only demand a client certificate when authentication is required. Set your TLS <Connector>'s clientAuth attribute to want and set your auth-method in web.xml to be CLIENT-CERT. That ought to do it.

Two way SSL authentication in Netty

I'm working on a Server and Client based app which require two way SSL authentication. (Client authenticates server and Server authenticate client both using SSL Certificates.)
I'm quite new to Netty and have few doubts regarding this.
Is two way authentication possible using Netty?
Can it be simply achievable by adding another SslHandler to the pipelinefactories of both server and client?
If the above is true, how can I grab the required SslHandler at the ChannelConnected() method to do the SslHandshake? And Is it possible to invoke the second handshake at the ChannelConected() method by calling the pipeline back again?
Are there any examples I could refer to regarding this?
I really appreciate any help regarding this, answers or a push in right direction.
Is two way authentication possible using Netty?
Yes
Can it be simply achievable by adding another SslHandler to the pipelinefactories of both server and client?
Yes
If the above is true, how can I grab the required SslHandler at the ChannelConnected() method to do the SslHandshake?
You need the setup the keystore and the truststore correctly when creating your SSLContext.
And Is it possible to invoke the second handshake at the ChannelConected() method by calling the pipeline back again?
From memory, client and server authentication is done in the 1st handshake.
On the client, install the client's private key in the keystore and the server's public key in the truststore.
On the server, install the server's private key in the keystore and the client's public key in the truststore.
Are there any examples I could refer to regarding this?
Here's an example I did for websockets. It only shows you how to setup the server keystore. You will have to add a truststore as the 2nd parameter of serverContext.init(kmf.getKeyManagers(), null, null);
Here's a similar example in Scala with trust store setup.
Here's a good java guide on how to setup SSLContext.
Hope this helps.
Two way authentication requires that both server and client have certificates that the other trusts. The client needs to generate a private key, store it in his keystore, and get it signed by somebody that the server's truststore trusts.
It isn't just a matter of what code you write.
SSL is a presentation layer protocol and the SSL handshake happens right after the socket connection is established and the before the application layer gets a usable socket connection. No matter what application you are using, if you have the SSL protocol layer in place then you can work over SSL.
Two way authentication is just a matter of configuration as mentioned by #EJP above. If both the parties can establish and validate each other trust chain then the handshake succeeds. Refer the netty configuration manual for configuring SSL truststores.

managing multiple entries in java (custom) truststore

I've read a few related questions, but none of them really helps me. So, let me give you a little background story:
I'm writing a voting-system in which you have couple servers that manage registration, exchange of vote cards, and voting. During the registration user submits his personal data, the server checks if the data match those in the database and then accepts the user's certificate and adds it to its trustStore like this:
KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert);
trustKeyStore.setEntry(alias, entry, null);
and then stores the trustStore in the file. During the registration a lot of users register their certificates, and then they connect to a different server that requests both client and server authentication, and here the problem arises. I thought that this 'other server' can use the aforementioned trustStore to perform the SSLHandshake with users, but it appears that the trustStore 'remembers' only the last user that registered. I'm creating SSLServerSocket like this:
tmf.init(trustKeyStore);
kmf.init(keyStore, password);
// create, init SSLContext
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
// create SSLSocketFactory and SSLSocket
SSLServerSocketFactory sslSocketFactory = (SSLServerSocketFactory)sslCtx.getServerSocketFactory();
sslSocket = (SSLServerSocket)sslSocketFactory.createServerSocket(1234);
As I mentioned before, both-side authentication works fine, but only for the last entry in the trustStore. So, now finally my question is - do I have to create a separate trustStore for each user? Do I have to overload TrustManager? Basically, is there any way I can make the SSLContext/SSLEngine iterate over all of my trusted certificates and go through with the handshake if it finds the one that matches?
UPDATE
I think I need to clarify a few things. First of all, this is not a web application, just a normal client-server java swing app. Every single certificate used (server or client) is self-signed, but the server's cert is build into the client application, so when the client connects to the server he can compare certificates (works). I have also tried to solve it exactly like Bruno suggested - after user registers, server checks if his data is valid, if it is it issues a new certificate for the client, adds it to his truststore and sends it to the client. But that didn't work even for the last registered client.
(Edited after changes to the question.)
Method 1:
One way to solve this would be to use a CA, possibly your own.
Method 2:
Accept any client certificate at the handshake level. Although accepting any server certificate from a client point of view is a bad idea, since it introduces the possibility of a MITM attack (see here and here, for example), accepting any client certificate during the handshake doesn't present the same problems.
At the end of the handshake, provided that the server certificate is still verified, you will know that:
The communication between the client and the server is established securely, as it would with HTTPS without client-certificate authentication.
The client has the private key for the certificate it has presented during the handshake. This is guaranteed by the TLS CertificateVerify message, which uses the client's private key to sign the concatenation of all messages exchanged during the handshake so far, including the server certificate and the client certificate. This doesn't actually depend on the (trust) verification of the client certificate itself, and will only work if the public key in the client certificate can validate the signature in the TLS CertificateVerify message.
With this, you will know that the client certificate the server gets is used by someone who has its matching private key.
What you won't know, is who that public key certificate belongs to, i.e. who the user at the other end is, because you're missing the verification step normally performed using a CA, which should itself rely on an out-of-band mechanism before issuing the certificate. Since they're using self-signed certificates, you would have to perform this out-of-band step anyway, perhaps via some information during the registration.
Doing this would allow you to manage your users and the way they use their certificates more easily. You could simply store and/or lookup the current user's certificate in a database common to your servers. You can get access to the certificate by looking at the SSLSession within your application. You will find the user certificate in position 0 of the array returned by getPeerCertificates(). You can then simply compare the credential you see (again, because the two-way handshake was successful) with those you've seen before, for the purpose of authentication (and authorization) within your application.
Note that even if you just accepted self-signed certs that you keep adding, you would still have to track this public key information as part of your system, in addition to the Subject DNs because you would have no control over the Subject DNs (two users could choose to use the same, intentionally or not).
To implement this, you will need to configure your server with an SSLContext which uses an X509TrustManager that doesn't throw any exception in its checkClientTrusted method. (If you're using the same SSLContext for other purposes, I would get the default PKIX TrustManager and delegate the calls to checkServerTrusted to it.) Since you're probably not going to know what the Subject/Issuer DN of these self-signed certificates are going to be, you should sent an empty list of accepted CA (*) by returning an empty array in getAcceptedIssuers() (see example here).
(*) TLS 1.0 is silent on this subject, but TLS 1.1 explicitly allows empty lists (with unspecified behaviour). In practice, it will work with most browsers, even with SSLv3/TLSv1.0: the browser will present the full list of certificates to choose from in this case (or pick the first one it finds it it's configure to select one automatically).
(I'm leaving what's more specific about HTTP here... A bit out of context now, but this might be of interest to others.)
Method 1:
You could integrate the certificate issuing as part of your initial registration. When the users register their details, they also apply for a certificate which is immediately issued by your registration server into their browser. (If you're not familiar with this, there are ways to issue certificate within the browser, using <keygen />, CRMF (on Firefox), or ActiveX controls on IE, which one depends on the version of Windows.)
Method 2:
In a Servlet environment you can get it in the servlet request javax.servlet.request.X509Certificate attribute
In addition, this tends to improve the user experience, since the rejection of a client certificate doesn't necessarily terminate the connection. You could still serve a web page (with an HTTP 401 status perhaps, although technically, it would need to be accompanied by a challenge mechanism, and there isn't one for certs) at least telling your user something is wrong. Handshake failures (which the client-cert verification within the handshake would cause in case of problem) can be very confusing for users who don't really know about certificates. The downside is that it's quite hard to "log out" (similarly to HTTP Basic authentication), because of lack of user interface support in most browsers.
For the implementation, if you're using Apache Tomcat, you may be interested in this SSLImplementation or this answer if you're using Apache Tomcat 6.0.33 or above.
I have solved the problem. As it often happens, there was a stupid mistake on my side - one of the servers were overwriting the whole keystore. I have spent a lot of time figuring how to set up this ssl communication and I haven't found much about it on the web, so I hope this will help somebody in the future.
In order to set up server-only communication you need to do the following:
1. On the client side obtain (somehow, in my case it was built in the client app) the server's certificate and add it to your truststore like that:
KeyStore clientTrustedKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
clientTrustedKeyStore.load(null, "password".toCharArray());
KeyStore.Entry entry = new KeyStore.TrustedCertificateEntry(cert);
clientTrustedKeyStore.setEntry("alias", entry, null);
While creating sslsockets either on the client or the server side, you need to 'feed' the SSLContext with your keyStore (server), or trustStore (client) to the SSLContext:
tmf = TrustManagerFactory.getInstance("SunX509");
kmf = KeyManagerFactory.getInstance("SunX509");
tmf.init(trustKeyStore);
kmf.init(keyStore, password);
// create, init SSLContext
SSLContext sslCtx = SSLContext.getInstance("TLS");
sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
and then create the sslsocketfactory and sockets.
After creating sockets (only useful on the server side) set authentication mode:
sslSocket.setNeedClientAuth(boolean);
In case of setting up the both-side verification, things you have to change are: authentication mode (obviously), adding certificates to the trustStores on both client and server sides.
The other issue with both-side verification is that the below scenario won't work (although, at least for me, it seems logical):
1. Server issues its own self-signed certificate, adds it to its trustStore and then sends it to the client for him to authenticate with during future connections.
Why won't it work? Because when the client gets the certificate he can only add it to his keystore like that:
ks.setCertificateEntry(alias,cert);
whichjust won't do when it comes to SSLHandshake, you have to authenticate yourself with the certificate added to your keystore with setEntry:
ks.setKeyEntry(alias,keyPair.getPrivate(),keyStorePassword,certChain);
where certChain is i.e.
Certificate[] certChain = {myCert};
That would be all, I'm sure for some people all this is quite obvious, but I hope it will help some SSL beginners like myself :)

SSL client - when is the certificate needed?

I have this:
SSLSocketFactory factory = HttpsURLConnection.getDefaultSSLSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket("www.verisign.com", 443);
This is failing on the 2nd line with a "Connection refused" error.
Now, would I have to install verisign's certificate in my trust store before I can even do the above? I was under the impression that I could connect to an SSL server and execute getPeerCertificates() to get the certificates. Is this not what our browsers do? Otherwise how would they know which signing authority to use?
(Obviously I'm using Verisign as an example. My real URL is far too fugly to use here...)
Connection refused means nothing was listening at the target host:port, or a firewall got in the way. This is logically and temporally prior to anything SSL does.
Have you checked that the remote service is actually up and running, and that you can connect to it? Perhaps the "Connection refused" error is actually a refused connection. :-)
Usually you don't need to install server's certificate on your computer explicitly. PKI works in the way that your system should be able to validate server's certificate without any prior knowledge about it. However this will work only when your server's certificate has it's roots in on of the "known CAs", i.e. certificate authorities, whose root or other certificates are already listed on the client system. If this is not the case (eg. you have a self-signed or some other custom certificate on the server), you really need to install the certificate on your client system before the mentioned classes can validate server certificate properly.
You can read about certificates and how they are used in SSL here.

Categories