3
votes

What I'm planning to do is writing a client-server application that uses a SSL (TLS) connection to exchange data.

As the client is downloadable and I can not guarantee access to the keystore I'm looking for a way to import certificates at runtime.

What I need:

  • A way to import the server's public key/certificate into the client application
  • A way to import the server's private key/certificate into the server application

What I found out so far:

// load the server's public crt (pem), exported from a https website
CertificateFactory cf = CertificateFactory.getInstance("X509");
X509Certificate cert = (X509Certificate)cf.generateCertificate(new
FileInputStream("C:\\certificate.crt"));

// load the pkcs12 key generated with openssl out of the server.crt and server.key (private) (if the private key is stored in the pkcs file, this is not a solution as I need to ship it with my application)
KeyStore ks = KeyStore.getInstance("PKCS12");
ks.load(new FileInputStream("C:\\certificate.pkcs"), "password".toCharArray());
ks.setCertificateEntry("Alias", cert);

TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
tmf.init(ks);

KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
kmf.init(ks, "password".toCharArray());

// create SSLContext to establish the secure connection
SSLContext ctx = SSLContext.getInstance("TLS");
ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

This does not work for me as I'm getting an error:

java.security.KeyStoreException: TrustedCertEntry not supported at ks.setCertificateEntry("Alias", cert);

Also, I think pkcs12 is used to store private keys which is not what I want.

I'm new to java and I'm really stuck with that problem now.

Thanks in advance,

Kazuo

2
This doesn't make any sense, this is not how SSL works. The peer public keys are exchanged in the protocol.President James K. Polk
Well I just needed to add the server certificate to the client's trusted store. Without using keytool. And that is how it's done. (See user384706's answer below)Kazuo

2 Answers

2
votes

Sun's implementation of #PKCS12 does not allow to store trusted certificates if are not part of the chain of the private key.
If you need to use #PKCS12 you have to switch to a different provider e.g. Bouncy Castle supports this.
If you do not have a requirement on keystore type you can switch to JKS which is java's keystore and allows to set trusted certificates (i.e. not part of the private key).
For JKS you can use the default provider i.e. SUN.
UPDATE:
So your code would have to change as follows:

//Create a temp keystore with the server certificate  

KeyStore ksTemp = KeyStore.getInstance("JKS");    
ksTemp.load(null, null);//Initialize it  
ksTemp.setCertificateEntry("Alias", cert);   
ByteArrayOutputStream bOut = new ByteArrayOutputStream();  
// save the temp keystore
ks.store(bOut, password);  

//Now create the keystore to be used by jsse   
Keystore store = KeyStore.getInstance("JKS");   
store.load(new ByteArrayInputStream(bOut.toByteArray()), password);  

Now you use the keystore store in your code which has the server's trusted certificate and not the private key.
From the comments in the code I noticed that you have a PKCS12 created using OpenSSL?
If you already have a p12 then you can not use "JKS" for KeyManager.
You will have to use PKCS12 and load it as PKCS12 to use it in the kmf.
So you will have to use 2 types in your app

2
votes

You don't need to create a certificate at runtime. You don't need to download a keystore to the client. You just need your server certificate signed by a recognized CA.

What you are proposing is insecure. The client has to acquire the certificates to trust via a different means than the channel than the certificate is authenticating. Otherwise there is no reason to trust the certificate, or the channel, or the certificate, ... PKI with CA certs takes care of all that. Your proposal just undermines the scurity built into PKI and SSL. Don't do this.