Fetch SSL certificate from local store using java into sslContext object - java

I need to perform a rest call by attaching the local ssl certificate.
I do not have any info about KeyStore. I just know there is a Certificate installed in my PC and I have to use the certificate based on details of certificate like "Serial number", "Issuer" etc which i can see in the certificate details in the personal certificate store.
I need to create SSLConnectionSocketFactory object which can be attached to rest call.
My question is how to create the SSLContext object?
SSLContext sslContext;// How to create this object and pass it to sslSocketFactory.
HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, hostnameVerifier);

You can create the SSLContext instance using this code snippet.
// Load Certificate
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
Certificate certificate = certificateFactory.generateCertificate(new FileInputStream(new File("CERTIFICATE_LOCATION")));
// Create TrustStore
KeyStore trustStoreContainingTheCertificate = KeyStore.getInstance("JKS");
trustStoreContainingTheCertificate.load(null, null);
trustStoreContainingTheCertificate.setCertificateEntry("ANY_CERTIFICATE_ALIAS", certificate);
// Create SSLContext
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(trustStoreContainingTheCertificate);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
System.out.println(sslSocketFactory);

Related

Two-way mutual SSL authentication

I've been tasked with implementing functionality in a Spring Boot REST API to contact another API (XML webservice). The outside API uses two-way SSL authentication. I've been given the correct certificate to implement on our side, and I've implemented the Java code. But whenever I run the code I get "Received fatal alert: handshake_failure". I've loaded the jks keystore into the SSLContext like this:
FileInputStream truststoreFile = new FileInputStream("/Users/myUser/Desktop/myProject/myProjectName/src/main/resources/keystore-name.jks");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore truststore = KeyStore.getInstance(KeyStore.getDefaultType());
char[] trustorePassword = "keyStorePassword".toCharArray();
truststore.load(truststoreFile, trustorePassword);
trustManagerFactory.init(truststore);
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
KeyManager[] keyManagers = {};//if you have key managers;
sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom());
Would I actually have to configure anything else to enable mutual two-way SSL from our API, acting like I client in this scenario? I thought I could just like the cert keystore and go. But maybe I need to do something else to enable this?
You are using the file shared with you in the wrong context. That file is a Keystore containing the client certificate and corresponding key.
TrustStore - Tells which CAs should be trusted by the client (you).
Keystore - Tells the server about the client (you).
In order for the mutual TLS handshake to pass through, you need to load the Keystore and set it in KeyManager like below.
// Load the Keystore
KeyStore keyStore = KeyStore.getInstance("JKS");
InputStream keystoreStream = new FileInputStream(pathToJKSFile);
keyStore.load(keystoreStream, keystorePassword.toCharArray());
// Add Keystore to KeyManager
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(keyStore, keystorePassword.toCharArray());
// Create SSLContext with KeyManager and TrustManager
SSLContext context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), null, new SecureRandom());
SSLSocketFactory sslSocketFactory = context.getSocketFactory();
// Now, use this SSLSocketFactory while making the HTTPS request

Is this a proper way to implement SSL Sockets in Java?

I am trying to implement secured Socket connections between a Spring Server and an Android Client but I found some design problems.
At the beginning I implemented the solution provided in this tutorial (it's in Spanish but easily understandable with Google Traductor). This tutorial secures Socket connections with a key in each side (one for server and one for client) and uses a trustedKeys.jks to store trusted keys in each side.
That means that for every client I should:
Create a new keystore for each new client
Add this new client key to trusted keys on server side
Add server keystore to each new client
This seemed unrealistic to me considering the growth of the number of clients.
I found another approach that suits better my requirements, it uses the server certificate as a public key (I think) and ciphers the data with it, that will be deciphered by the server keystore:
Server Code
public SSLServerSocket getSSLServerSocket(int port) throws IOException, ...
{
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(resourceKeyFile.getInputStream(), keystorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keystore, keystorePassword.toCharArray());
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(kmf.getKeyManagers(), null, null);
SSLServerSocketFactory sslServerSocketfactory = sc.getServerSocketFactory();
return (SSLServerSocket) sslServerSocketfactory.createServerSocket(port);
}
Here is my function for creating a SSLServerSocket. In the KeyManagerFactory initialization kmf.init(keyStore, keystorePassword.toCharArray()); I set the server keystore keystore with its password keystorePassword which are read with #Value Spring Annotation outside the function. This function will return the SSLServerSocket which will accept client connections in a new Thread like this:
while(true) {
SSLSocket clientSocket = (SSLSocket) serverSocket.accept();
}
Client code
SSLContext context = socketUtil.createSSLContext();
SSLSocketFactory sf = context.getSocketFactory();
SSLSocket socket = (SSLSocket) sf.createSocket(serverUrl, port);
This code creates the context I want to usem which is presented below, and creates the Socket to an address serverUrl and a port port:
public final SSLContext createSSLContext()
throws Exception {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream in = getClass().getResourceAsStream("serverCert.pem");
KeyStore trustStore = KeyStore.getInstance("JKS");
trustStore.load(null);
try {
X509Certificate cacert = (X509Certificate) cf.generateCertificate(in);
trustStore.setCertificateEntry("serverKey", cacert);
} finally {
IOUtils.closeQuietly(in);
}
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(trustStore);
SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, tmf.getTrustManagers(), new SecureRandom());
return sslContext;
}
Here, I load the Server certificate serverCert.pem which I generated from the keystore with the keytool utility. Then other parameters are setted, such as the algorithm or the protocol. Thus, the socket will trust connections from the issuer of the certificate.
The code is working just nice and smooth, but I'm concerned about security and good practices. So once again, is this a good approach to solve my problem?
Any clarifications about the process, code or misunderstandings are welcome. Thank you so much!

Java Security set Provider to SSLContext

How can I set the Security Provider for a SSL Context?
My Code is like
Security.insertProviderAt(new XXX(), 1);///XXX is a Security Provider
KeyStore clientStore = KeyStore.getInstance("PKCS12");
clientStore.load(new FileInputStream(p12FileName), p12FilePass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(clientStore, p12FilePass.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kms, null, new SecureRandom());
If I remove the Provider XXX or set SunEC as the priority provider, everything works fine. But I can't do both due to some reason. Is there any way I can set the provider as SunEC in the SSL Context for the REST call without changing the first line Security.insertProviderAt(new XXX(), 1)

Extract certificate from SSLContext

I'm creating SSLContext in standard way:
take .p12 certificate file,
create KeyStore and load certificate into it,
create KeyManagerFactory, init it with KeyStore, and get KeyManagers,
create TrustManagerFactory, init it with null, and get TrustManagers.
create SSLContext and init it with KeyManagers and TrustManagers.
The question is - how can I extract KeyStore and certificate data back from SSLContext? The task is to obtain fingerprint hash from certficate.
Is it even possible or I have to get it separately, reading certificate from file?
It can be done if you have a custom TrustManager. You can refer to this link for that custom class. Look for the private SavingTrustManager static class.
And the place where you are using the java's default TrustManager, use this class so that you can retrieve the certificate that the server sent.
SSLContext context = SSLContext.getInstance("TLS");
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(dummyTrustStore);
X509TrustManager defaultTrustManager = (X509TrustManager) tmf.getTrustManagers()[0];
SavingTrustManager savingTrustManager = new SavingTrustManager(defaultTrustManager);
context.init(null, new TrustManager[] { savingTrustManager }, null);
SSLSocketFactory factory = context.getSocketFactory();
And after you have started the handshake, you can get the certificates from the SavingTrustManager from the static member variable chain, like:
savingTrustManager.chain

How to enable https with org.jboss.resteasy.plugins.server.netty.NettyJaxrsServer?

I have encountered a problem when I try to set up a https server with resteasy-netty4 (http service is OK)
resteasy version 3.0.16.Final
java version 1.8
By searching from stackoverflow and google, I got some solutions, such as
Simple Java Https Server.
So, the original demo is running successful, but unfortunately, it didn't work after integrating with NettyJaxrsServer.
I created a sslcontext as below:
public SSLContext getSSLContext1() throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
// initialise the keystore
char[] password = "password".toCharArray();
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("{PARENT_PATH}\\testkey.jks");
ks.load(fis, password);
// setup the key manager factory
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, password);
// setup the trust manager factory
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
// setup the HTTPS context and parameters
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
return SSLContext.getDefault();
}
and call org.immortal.hydra.gateway.server.JaxrsGatewayServer#setSSLContext to enable https server.
It was okay to start up, but failed to serve.
Caused by: javax.net.ssl.SSLHandshakeException: no cipher suites in common
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1666)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:304)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:292)
at sun.security.ssl.ServerHandshaker.chooseCipherSuite(ServerHandshaker.java:1036)
at sun.security.ssl.ServerHandshaker.clientHello(ServerHandshaker.java:739)
at sun.security.ssl.ServerHandshaker.processMessage(ServerHandshaker.java:221)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:979)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:919)
at sun.security.ssl.Handshaker$1.run(Handshaker.java:916)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.ssl.Handshaker$DelegatedTask.run(Handshaker.java:1369)
at io.netty.handler.ssl.SslHandler.runDelegatedTasks(SslHandler.java:1124)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1009)
If you have any suggestions to fix this, please let me know.
It looks like you're setting up the SSL context but then returning the default one. Try changing
return SSLContext.getDefault();
to
return sslContext;
Also it's useful to learn how to debug ssl by enabling SSL logging: http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/ReadDebug.html

Categories