Is this a proper way to implement SSL Sockets in Java? - 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!

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

TLS Connection with no Keystore

I am stumped as to how my client program is able to create an SSLSocket and connect to a server program successfully even though the keystore file (sessionKeyStore below) is null.
The following code will successfully create a TLSv1.2 socket with a null keystore and successfully handshake with my server program which I thought always requires a key exchange.
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(sessionKeyStore); // sessionKeyStore IS NULL!!
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(sessionKeyStore, SSL_PASSWORD.toCharArray()); // password is not null
SSLContext sslContext = SSLContext.getInstance("TLSv1.2");
SecureRandom sc = createSecureRandom();
sc.setSeed(System.nanoTime());
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), sc);
SSLSocketFactory factory = sc.getSocketFactory();
SSLSocket socket = (SSLSocket) factory.createSocket(server, serverPort);
((SSLSocket) socket).startHandshake();
I didn't realize that client authentication is not included in the basic TLS handshake. Only the server is authenticated. Client authentication is a separate handshake that occurs after the server is first authenticated and only if the server requests it. The problem was I needed to specifically tell the Server Socket to demand that the client certificate verification be included in the TLS handshake. The way to do that in Java JSSE is by adding the following code to the server side:
SSLContext sslContext = getSSLContext();
SSLServerSocketFactory sslserversocketfactory = sslContext.getServerSocketFactory();
SSLServerSocket sslserversocket = (SSLServerSocket) sslserversocketfactory.createServerSocket(port);
// Add the below to force a certificate request on the client.
sslserversocket.setNeedClientAuth(true);
https://www.comparitech.com/blog/information-security/tls-encryption

How do you configure the Apache httpcore5 java HttpAsyncServer to use TLS on its listening port?

When using the apache httpcore5 java library to implement an HTTP server I can not find the proper idiom for rigging the socket to use TLS.
Under the older httpcore version 4.x I could
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(keystoreStream, keystorePassword.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, keyPassword.toCharArray());
SSLContext sc = SSLContext.getInstance("TLSv1.2");
sc. init(kmf.getKeyManagers(), null, new SecureRandom());
ServerSocket sSock = sc.getServerSocketFactory().createServerSocket(port);
and then accept connections from that server socket and bind() those sockets to a DefaultHttpServerConnection and handleRequest() in a loop.
I have not yet found an example of how to write a TLS-capable server using the httpcore5 framework.
After some rummaging around I found H2ServerBootstrap.setTlsStrategy() and used it like this:
SSLContext ctx = buildAppSSLContext();
bootstrap.setTlsStrategy(new BasicServerTlsStrategy(ctx, new FixedPortStrategy(new int[]{port})));

Fetch SSL certificate from local store using java into sslContext object

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

ActiveMQ - create embedded broker listening on SSL (TLS) transport error

I would like to create an embedded ActiveMQ broker that listens on SSL protocol using client authentication mechanism (TLS).
Here's my code that expects to do so :
//loading keystore from file
KeyStore keystore = KeyStore.getInstance("pkcs12");
File ksfile = new File("/home/me/client1.pkcs12");
FileInputStream ksfis = new FileInputStream(ksfile);
keystore.load(ksfis, "password".toCharArray());
//loading truststore from file
KeyStore truststore = KeyStore.getInstance("jks");
truststore.load(new FileInputStream(new File("/home/me/client1.truststore")), "password"
.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory
.getDefaultAlgorithm());
kmf.init(keystore, "password".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(truststore);
//broker definition
String cfURI = "ssl://localhost:2032";
BrokerService brokerService = new BrokerService();
brokerService.addConnector(cfURI);
//configure ssl context for the broker
SslContext sslContext = new SslContext(kmf.getKeyManagers(),tmf.getTrustManagers(), null);
//need client authentication
sslContext.getSSLContext().getDefaultSSLParameters().setNeedClientAuth(true);
sslContext.getSSLContext().getDefaultSSLParameters().setWantClientAuth(true);
brokerService.setSslContext(sslContext);
brokerService.start();
When i execute the previous code in a main program, i get the following error :
GRAVE: Could not accept connection : javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled.
Any suggestions are wellcome !
Thanks for reading.
Has your client set the certificate from the broker in its truststore? I'm afraid thats the problem you are running into.
Other than that, it would probably help if you paste the client code as well
I got this error by using ActiveMQConnectionFactory instead of ActiveMQSslConnectionFactory when connecting from the client

Categories