managing multiple entries in java (custom) truststore - java

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 :)

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.

Https vs ssl pinning

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.

Get certificate by alias in keystore with multiple entries in Java

Ok! We are trying to implement a client server aplication (chatroom) . Of course the server is multithreaded. We wanted the communication to be secure so we used ssl sockets and certificates. I have read that you can store multiple certificates and keys in one keystore. When a client enters the chat he needs to fill in his username.
Do we have to connect the username to the alias of the certificate/key?
If yes, how can we use the specific certificate/key from the keystore from the alias? Or is there another way? I mean how can we "pick" the specific certificate depending on the name
Is there a way for the clients to create their certificates at the time of they enter? (We want the certificates to be signed by a CA we have already implemented)
Thank you!
Basically what you want is Mutual or 2 way SSL. Read these for more information - here and here
In short - the SSL communication works (in context of certificates for authentication) is server will send the certificate to the client and if that certificate is present in the client's certificate store or Java's keystore in your case, then it authenticates the server.
Typically server never asks client to send certificate but in your case you wants it so it makes it Mutual or 2 way SSL. So, while handshake process, server will ask client also to send its certificate and it will also check in its keystore if that certificate is present, if so then it will be happy else it will end SSL handshake.
What you need:
Your each client and your server should have a valid certificate.
Your server should have those client certificate present in its "trust keystore", so that it can authenticate client.
Your each client should have server's certificate in its "trust keystore", so that it can authenticate server.
Your server should be configured to support 2 way SSL. Read here for Weblogic.
Answering your questions specifically:
Do we have to connect the username to the alias of the
certificate/key?
No, only this you want is that client certificate should present in the server's "trust keystore". But since your client app and server is on same machine, so I would recommend that have different JVM's installations to run client and server so that you have support different certificates.
If yes, how can we use the specific certificate/key from the keystore
from the alias? Or is there another way? I mean how can we "pick" the
specific certificate depending on the name
Not applicable.
Is there a way for the clients to create their certificates at the
time of they enter? (We want the certificates to be signed by a CA we
have already implemented)
Certificate should be prepared beforehand, and certificate creation and signing is a complex process, you have to generate a CSR etc.
Please do read my this answer for other details you may require while doing all this.

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.

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