Writing a client-server application that uses ssl / tls using java without beeing able to use keytool
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.getInstan开发者_如何学运维ce("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
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
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.
精彩评论