Is storing a private key possible to be secure? - java

This question is about a project of mine. I have a server-side application running on my Raspberry Pi, while the client-side application is supposed to be distributed on all my other devices. The following exzerpt is showing the code of the client-side.
I've been digging the Internet and what I have gathered is that in the end there no 100% safe way to store a private key. However, it is considered good practice to safe all keys in a KeyStore. The following is my solution to it, but my problem is: the KeyStore-file, client.jks, lies inside the JAR, together with the hardcoded password. client.jks contains the server-side's public key and the client-side's private key.
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(new FileInputStream(KEYSTORE), PASSWORD.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");
keyManagerFactory.init(keyStore, PASSWORD.toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("SunX509");
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocket connection = (SSLSocket) sslContext.getSocketFactory().createSocket("localhost", 6789);
System.out.println("Connection successful!");
The Strings KEYSTORE and PASSWORD contain the KeyStore-file and the password, both hardcoded.
Is this really the best practice or am I missing something? Is the new SecureRandom object created for every connection enough to make the connection safe?
Best regards and thank you for your answers!

Related

Convert Program to use Secure Socket Connection via java-websockets library

I am attempting to convert an existing application that uses the org.java-websocket library to allow the webserver to communicate using https instead of the previous http. After researching, the only example I was able to find is here:
https://github.com/TooTallNate/Java-WebSocket/blob/master/src/main/example/SSLServerExample.java
public static void main(String[] args) throws Exception {
ChatServer chatserver = new ChatServer(
8887); // Firefox does allow multible ssl connection only via port 443 //tested on FF16
// load up the key store
String STORETYPE = "JKS";
String KEYSTORE = Paths.get("src", "test", "java", "org", "java_websocket", "keystore.jks")
.toString();
String STOREPASSWORD = "storepassword";
String KEYPASSWORD = "keypassword";
KeyStore ks = KeyStore.getInstance(STORETYPE);
File kf = new File(KEYSTORE);
ks.load(new FileInputStream(kf), STOREPASSWORD.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, KEYPASSWORD.toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
SSLContext sslContext = null;
sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
chatserver.setWebSocketFactory(new DefaultSSLWebSocketServerFactory(sslContext));
chatserver.start();
}
The only problem with this is that I'm hesitant because it seems to be wanting you to access the keystore.jks and provide to the store password + keypassword and also seems to expect the KeyStore file to be on the running computer (or somewhere released with the software). Isn't this a security risk?
I already have the jar signed with the keystore, perhaps there is nothing more that I need to do? Can someone point me to a different example if this is not the way to do this?
This keystore I'm using is the one provided to us by an external company to our company to sign our java applications. Perhaps this is not the keystore I should be using and need to make one for this single app independently of that one?

Initialising SSLContext in case of multiple threads

I am trying to initialise a SSLContext with a custom truststore and keeping keystore and secureRandom as default. For the custom truststore I have a JKS file which I will use to initialise TrustManagers.
This is a server application which will get multiple concurrent requests. I am not able to understand if we should use a singleton SSLContext/SSLSocketFactory or not. Based on Are SSLContext and SSLSocketFactory createSocket thread safe?, it looks like we should create new objects for every request. But then, the below implementation will cause high latency for loading truststore and creating SSL object for every request. Is this understanding correct?
Also, it will be very helpful if I could understand how to check if a particular object can be singleton in such cases? In the documentation I generally look for mention about being thread safe, but I could not find any such information in oracle documentation for SSLContext/SSLSocketFactory.
URL url = new URL(endpoint);
connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(getDefaultSSLSocketFactory());
public SSLSocketFactory getDefaultSSLSocketFactory() {
String path = "path to truststore";
try (FileInputStream fis = new FileInputStream(path)) {
SSLContext sslcontext = SSLContext.getInstance("TLSv1.2");
KeyStore clientKeyStore = KeyStore.getInstance("JKS");
clientKeyStore.load(fis, "password".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(clientKeyStore);
sslcontext.init(null, trustManagerFactory.getTrustManagers(), null);
return sslcontext.getSocketFactory();
}

One-Way SSL Authentication as using Netty

I'm trying to build a one-way authentication socket server using Netty.
First I used keytool to generate keystore, self signed certificate, truststore for both server and client, and I wrote some code in my server/client, the SSL authentication is working.
Here is my question:
Is there any way that I don't need to add truststore to my client, I only add the keystore to my server, and it would still work well? I thought one-way authentication means that only server holds the certificate?
The following is what I wrote in my server/client to add the SslHandler so far:
server:
private void addSslHandlerOneWay(SocketChannel ch) throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(new File("svrks.jks")), "kspassword1".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "kspassword2".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(false);
sslEngine.setNeedClientAuth(false);//one-way
sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols());
sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
sslEngine.setEnableSessionCreation(true);
ch.pipeline().addFirst("SSL", new SslHandler(sslEngine));
}
client:
private void addSslHandlerOneWay(SocketChannel ch) throws Exception {
SSLContext sslContext = SSLContext.getInstance("TLS");
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(getInputStream("clits.jks"), "tspassword2".toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ts, "tspassword1".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ts);
sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(true);//client
sslEngine.setEnabledProtocols(sslEngine.getSupportedProtocols());
sslEngine.setEnabledCipherSuites(sslEngine.getSupportedCipherSuites());
sslEngine.setEnableSessionCreation(true);
ch.pipeline().addFirst("SSL", new SslHandler(sslEngine));
}
Thanks, guys.
I thought one-way authentication means that only server holds the certificate?
The server needs to authenticate itself with the certificate. For this it needs certificate and matching private key.
The client needs to verify the authentication, i.e. that the certificate send by the server is actually the expected one. For this it needs to know either the certificate itself or the CA which issued the certificate - which is the same in case of self-signed certificates.
What you seem to expect is that the client does not need any previous knowledge of the servers certificate or the issuer CA. If this would be the case then the client would just accept any certificate, both corrects ones from the server and also fake ones from an attacker. Without previous knowledge (i.e. a local trust anchor) what to expect the server cannot distinguish between correct and fake certificates.

Validation of a certificate chain when opening a SSLSocket in Java

My Setup
My goal is to set up a SSL/TLS secured connection (explicit) with an FTP-Server.
The appropriate Root CA Certificate is stored in a Truststore called truststore.jks.
After the AUTH TLScommand I'm using the following code to build up the SSLSocket.
public SSLSocket enterSecureMode(Socket s) throws Exception {
KeyStore truststore = KeyStore.getInstance("JKS");
truststore.load(Files.newInputStream(Paths.get("truststore.jks")), "mypass".toCharArray());
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(truststore);
SSLContext sCon = SSLContext.getInstance("TLS");
sCon.init(null, tmf.getTrustManagers(), null);
SSLSocketFactory sslSocketFactory = sCon.getSocketFactory();
return (SSLSocket) sslSocketFactory.createSocket(s, "<HOSTNAME>", 21, true);
}
The code itself is running fine and I received a secured Socket-Connection, but I wonder whether this can stand attacks like e.g MITM or not. I mean would that program discover an attempt of somebody trying to 'give me a Fake-Certificate'.
Therefore I'd be very happy if some more experienced SSL-Network-Programmers could enlight me :D
This is sufficient. The attacker would have to provide a certificate signed by the root CA. However you don't need all this code: you only need
System.setProperty("javax.net.ssl.trustStore", "truststore.jks");
SSLContext sCon = SSLContext.getDefault();
SSLSocketFactory sslSocketFactory = sCon.getSocketFactory();
return (SSLSocket) sslSocketFactory.createSocket(s, "<HOSTNAME>", 21, true)
If you want to be totally paranoid, after creating the SSLSocket you can get the SSLSession and then the peer certificate chain and make sure that the zeroth entry exactly matches the exact server's certificate, but this step is mostly omitted.

using Certificate in https

I write android application.
How can I use Certificate in https connection when I initialize certificate from directory file and not from packages?
When I have packages file with password, this code works:
KeyStore keyStore = KeyStore.getInstance("PKCS12");
keyStore.load(certificateIs, pass.toCharArray());
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(keyStore, pass.toCharArray());
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(kmf.getKeyManagers(), trustAllCerts, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
But I have certificate initialized from der file:
CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate certificate = (X509Certificate) cf.generateCertificate(certBytes);
I do not know how use this certificate over https connection.
You seem to be talking about client-certificate authentication (where your Android device is the client).
Firstly, you need the client to have the private key matching the public key in the certificate you're trying to use (that's the whole point, otherwise, it wouldn't authenticated anything). PKCS#12 is one of the usual formats for containing the private key and the certificate. If you only have the certificate in a der file, you probably won't have the private key in it, hence it won't work.
It's not quite clear from your question what you do with your certificate variable, with respect to the KeyManagerFactory (if you have a custom X509KeyManager, it should return the private key in its getPrivateKey method, otherwise it won't work).
Secondly, client-certificate authentication is always initiated by the server, so you'd need the server to be set up accordingly too (it seems to be the case already, if your test based on a PKCS#12 keystore works).

Categories

Resources