Java Client to connect to Server with Openssl and Client Auth - java

I have to write a Java Client to connect to an SSL server. The server uses openssl certificate, and is configured to do Client Auth.
I can't seem to locate any useful resources online that can help me (who doesn't know anything about openssl and much about SSL) to understand who to go about implementing my Client Side.
Help!

The twist here is that you are using client authentication, so you need a private key and a certificate to identify yourself. You provide this to JSSE by specifying KeyManagers when you initialize an SSLContext.
Customizable Setup
Following are the basic steps. The JSSE API was significantly improved in Java 6, but I'll stick with Java 5, in case you're stuck on that version.
KeyStore tks = KeyStore.getInstance(KeyStore.getDefaultType());
tks.load(...); /* Load the trust key store with root CAs. */
TrustManagerFactory tmf =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(tks);
KeyStore iks = KeyStore.getInstance(KeyStore.getDefaultType());
iks.load(...); /* Load the identity key store with your key/cert. */
KeyManagerFactory kmf =
KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(iks, password);
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SocketFactory factory = ctx.getSocketFactory();
Socket socket = factory.createSocket(host, port);
System Configuration
An alternative "zero-config" scenario can be used when using the SunJSSE provider. I believe many other providers (like IBM) have followed the same pattern and will work as well. The mechanism uses system properties, and is described in detail by the JSSE Reference Guide.
For client authentication, the important properties are javax.net.ssl.keyStore and javax.net.ssl.keyStorePassword. The values should be the path to the user's key store and the password for that key store's "key entries", respectively.
When using these properties, you can create a new SSLSocket that supports client authentication like this:
SocketFactory factory = SSLSocketFactory.getDefault();
Socket socket = factory.createSocket(host, port);
Since you are using the "default" SSLSocketFactory, which depends on the system-wide properties, all sockets created in the JVM will authenticate with the same certificate. If you need more control than that, you have to use the "Customizable Setup" above.

Java includes SSL support in the standard API. Have a look at these classes in the 1.5.0 javadoc:
SSLSocket if you're doing the comms logic yourself.
HttpsURLConnection if the server side speaks HTTP

You could use httpclient. Have a look at this SSL guide.

Related

force-sending client certificate

I'm using a Java SE Jersey client to connect to a HTTPS resource which uses two-way SSL.
My SSLContext looks like this:
private SSLContext getSSLContext() {
SslConfigurator sslConfig = SslConfigurator.newInstance()
.keyStoreFile("src/main/certificates/testcert.p12")
.keyPassword("mypassword");
return sslConfig.createSSLContext();
}
The problem is that the client certificate is never sent.
I get error "Warning: no suitable certificate found - continuing without client authentication" and I've tracked the reason to the fact that the client certificate isn't issued to one of the Cert Authorities listed in the server's CertificateRequest message to the client. I know from testing with cURL that the server will accept the certificate regardless. The endpoint is a public test system.
My question: How do I force-send my client certificate? (i.e. my Java SE client should ignore the fact that the testcert.p12 certificate's issuer is not the list of issuers that the server has said it would accept)
Please, don't point me to answers that are about disabling check of the server's certificate or about using self-signed certificates. The server's certificate is just fine.
UPDATE
It turned out my problem was another one. I debug by setting system property javax.net.debug=all. After examining the resulting output it looked to me as if the keystore was empty, even after doing the above. So no wonder why "no suitable certificate found".
Jersey has this 'clever' SslConfigurator class which is there to help you set up an SSLContext. Perhaps just too clever for me, because I couldn't make it work with the above code. So instead, I now configure my SSLContext like below:
KeyStore ks = KeyStore.getInstance("PKCS12");
FileInputStream fis = new FileInputStream("src/main/certificates/testcert.p12");
ks.load(fis, "mypassword".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "mypassword".toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), null, null);
// now use 'sc' in Jersey
This works for me where Jersey's helper class didn't. I fully sympathize with Jersey's idea of a helper class for SSLContext, because JSSE seems overly complex here for such a simple use case. Well, well.
You can't. It would be a TLS protocol violation, and therefore there is no API to support it. The various TLS APIs will only send a client certificate if:
It was requested, and
A client certificate can be found that conforms to what is specified in the CertificateRequest message.
You will have to arrange for your server to trust your client certificate, either by getting it signed by a CA or by exporting it to the server's trusted certificate store, whatever form that takes at the server.

Securing communication between multiplatform clients (Desktop and Android primarily) with desktop server through SSL/TLS

I went through the JSSE Reference Guide + Stack Overflow most common explanations about SSL/TLS and I haven't really found anything that encapsulates everything what I need.
The project that I'm currently working on is about connecting multiplatform clients (Android or Desktop) on a desktop server which is connected to the robot (it is working in the local network which has parallel TCP and UDP connection -> TCP is used for sending some kind of commands for the robot from the client to the server and UDP is used for updating the robot position on the map which is sent from the server to the client).
Maybe I was a little half-cocked so I tried to implement the Android piece of code without really informing myself about everything that possibly could go wrong. The code below represents the code on the server side (which is desktop application) and client side (android application).
Server:
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(new FileInputStream(PATH_TO_KEYSTORE), PASSWORD.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(keystore, PASSWORD.toCharArray());
SSLContext context = SSLContext.getInstance("TLS");
KeyManager[] keyManagers = kmf.getKeyManagers();
context.init(keyManagers, null, null);
SSLServerSocketFactory factory = context.getServerSocketFactory();
serverSocket = (SSLServerSocket) factory.createServerSocket(port);
serverSocket.setSoTimeout(0);
Android client:
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(Gdx.files.internal(PATH_TO_KEYSTORE).read(), PASSWORD.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(keystore);
SSLContext context = SSLContext.getInstance("TLS");
TrustManager[] trustManagers = tmf.getTrustManagers();
context.init(null, trustManagers, null);
SSLSocketFactory sslSocketFactory = context.getSocketFactory();
sslSocket = (SSLSocket) sslSocketFactory.createSocket(address, port);
Variable PATH_TO_KEYSTORE is a path to the file which was generated using this command keytool -genkey -keystore server.keystore. I know that I shouldn't have the same keystore for server and client, but I don't know how can I do it the other way which is one of my questions too.
After executing this piece of code, exception happened which said Wrong version of key store. and then I figured out that I am totally lost.
Anyway, I have several questions that bother me in this matter:
Is it possible to secure communication both for TCP and UDP using the SSL/TLS?
Is it possible to create a uniform way to generate keystores that will work for every platform (since I figured out on an example above that Android does not support JKS)?
If the answer on question No. 2. is NO, how can I secure the communication in separate ways (how to generate keystores for Android and Desktop version separately)?
When everything works out like it should (in a static way), is there a way to generate keystore for client dynamically, in other words: how can one generate his private key in the Android piece of code (which ultimately means that every client will have its own generated private key)?

Java: establish SSL connection without authentication

I am trying to create an application where I have a server that hosts files and a client that wants to retrieve those files.
I am currently trying to establish a SSL connection between the client and the server, however, I only want the encryption part of SSL not the authentication (I do not want to go through the troubles of creating and managing keystores etc).
All the sample code I have found has used TLS which requires authentication. I was wondering what implementation does not use authentication? I read that there is a Diffie Hellman handshake but I was wondering if there was any other cipher suite besides TLS I could use?
SSLSocketFactory factory = null;
try {
SSLContext ctx;
KeyManagerFactory kmf;
KeyStore ks;
char[] passphrase = "passphrase".toCharArray();
ctx = SSLContext.getInstance("TLS");
kmf = KeyManagerFactory.getInstance("SunX509");
ks = KeyStore.getInstance("JKS");
is there a different SSLContext I could use that will not require me to create a KeyManagerFactory or a Keystore?
If you really want this and you have some other means of avoiding the man in the middle attacks you will now be vulnerable to, just enable the anonymous cipher suites.

How can a Java client use the native Windows-MY store to provide its client certificate as required during a SSL handshake?

This question was also posted at TLS client authentication fails when using SunMSCAPI but has not found an answer.
I have a Java6 application running on a Windows Win2008R2 client that connects to a server requiring client certificates. The Java application is required to use the native Windows stores, i.e. Windows-Root and Windows-My, and employs the SunMSCAPI provider. The Windows certificate management console reports that both the client cert and CA cert that signed it are correct and enabled for all purposes.
Server verification works exactly as expected, but client authentication fails when the client throws a SSLException complaining that the privatekey is not an RSA private key. However, when the Java default provider is used instead, and the same client certificate is taken from a JKS keystore, i.e. SunMSCAPI and the Windows stores are not used, client authentication works just as expected and the SSL connection succeeds.
When executed, the application reports that it's using the SunMSCAPI provider and is able to print the correct client certificate as well as information about its RSAPrivateKey. Tracing indicates that the client exception occurs after the servers 'Hello Done' as its response to the server's certificate request is being prepared.
The relevant code bits for the client keystore portion of this are:
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyManagerFactory kFac = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
Provider pvdr = new sun.security.mscapi.SunMSCAPI();
Security.insertProviderAt(pvdr,1);
KeyStore kStore = KeyStore.getInstance("Windows-MY",pvdr);
kStore.load(null,null);
kFac.init(kstore,null);
sslContext.init(kFac.getKeyManagers(), <a trust factory>.getTrustManagers(), new java.security.SecureRandom());
SSLSocketFactory sockFactory = SSLSocketFactory(sslContext);
SSLSocket sslSock = (SSLSocket)sockFactory.createSocket(<some destination host>, <some destination port>);
BufferedInputStream bInStr = new BufferedInputStream(sslSock.getInputStream());
bInStr.read(<the read arguments>); <<< exception thrown in here
Any pointers or suggestions will be greatly appreciated!
The stacktrace shown in your link shows the problem to be at RSA's JSSL provider. Especially for this case you should use Sun/Oracle's JSSE provider. It's the default one anyway.
Call it as
SSLContext sslContext = SSLContext.getInstance("TLS", "SunJSSE");

How to make 'simple SSL' thru Web Services?

I know how to secure Web Services using certificates. that's my client code:
SSLContext ssl = SSLContext.getInstance("SSLv3");
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
KeyStore store = KeyStore.getInstance(KeyStore.getDefaultType());
String password = Configuration.getConfig("keyStorePassword");
store.load(new FileInputStream(new File(Configuration.getConfig("keyStore"))), password.toCharArray());
kmf.init(store, password.toCharArray());
KeyManager[] keyManagers = new KeyManager[1];
keyManagers = kmf.getKeyManagers();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(store);
TrustManager[] trustManagers = tmf.getTrustManagers();
ssl.init(keyManagers, trustManagers, new SecureRandom());
HttpsConfigurator configurator = new HttpsConfigurator(ssl);
Integer port = Integer.parseInt(Configuration.getConfig("port"));
HttpsServer httpsServer = HttpsServer.create(new InetSocketAddress(Configuration.getConfig("host"), port), 0);
httpsServer.setHttpsConfigurator(configurator);
Implementor implementor = new Implementor(); // class with #WebService etc.
HttpContext context = (HttpContext) httpsServer.createContext("/EventWebService");
Endpoint endpoint = Endpoint.create( implementor );
endpoint.publish(context);
Now, how to make 'simple SSL' ? How to make SSL connection without storing certificate on the client side. (Like connecting thru HTTPS in browser)
Java Runtime Environment does come with a lots (most widely used) Certificate Authorities in cacerts file. If the certificate you used to secure your service is signed by one of those root CAs, then you need not worry about sharing any certificate with clients.
However if you used self-signed certificate, and you don't want to pass/import certificate in truststore then you can implement custom X509TrustManager and create custom SSLContext for your connections. More details in this blog.
Self-signed certificate are useful for development and test environments but you really should consider getting your server certificate signed from a recognized Certificate Authority like Verisign, Thwate etc.
If I understand you correctly, then you want to have only server-side authentication much in the same way as if you connected to an https site in your browser, without requiring your clients to manage any certificates.
Your clients would connect as usual, simply replacing an http for an https in the connection URL. Java manages its own set of "default trusted root CA authorities" in the form of cacerts, a JKS keystore file located in $JRE HOME/lib/security. If you buy a certificate from any CA whose issuing certificate roots in one of the certificates contained in cacerts, then the client's certificate validation will automagically succeed. Google for "SSL/TLS server certificate" and you will find suitable vendors.
If you would use a self-issued certificate on the other hand, then there's no way to make certificate validation succeed on the client other than importing your self-made certificate in the client's certificate trust store. But that's why a "real" SSL/TLS certificate costs money and your self-issued certificate doesn't - anyone can generate their home-grown certificates, but trusting them is an entirely different story.
You can control if the https server requires client certificates in this way:
HttpsConfigurator cfg = new HttpsConfigurator(sslCtx){
public void configure(HttpsParameters params) {
SSLParameters sslparams = getSSLContext().getDefaultSSLParameters();
// Modify the default params:
// Using this, server will require client certs
//sslparams.setNeedClientAuth(true);
// Using this, server will request client certs. But if not available,
// it will continue anyway.
sslparams.setWantClientAuth(true);
params.setSSLParameters(sslparams);
}
};
HttpsServer httpsS = HttpsServer.create(new InetSocketAddress(8081), 50);
httpsS.setHttpsConfigurator(cfg);
If client certs are not required, clients can connect without client certificate, so simple calling https will work.
In my blog you can see example of client for how to bypass the server certificate and hostname validation (although not recommended, useful e.g. for testing)
http://jakubneubauer.wordpress.com/2011/09/06/java-webservice-over-ssl/
Just make the connection with HTTPS. As long as the client is using standard trusted certs it will work just fine. If they have a self signed cert you will need to to import the cert into the java keystore.
HTTPS in browsers works because there is a truststore containing SSL certificates on the client. In other words: There are certificates stored on the client side.
If you want HTTPS without any certificate stored on the client side, I think you should have a look at this article, which explains how to turn off the default certificate validation on HTTPS connection.

Categories