The following test setup runs fine on the Android emulator. It opens a SSL/TLS based connection to an
external server using mutual authentication:
ca.crt (to verify the server certificate):
—–BEGIN CERTIFICATE—–
BASE64 ENCODED STUFF
—–END CERTIFICATE—–
client.p12 (including a client certificate signed by a private CA which is trusted by the server): PKCS#12 format
The Java/Android code which runs succesfull:
trustStore = KeyStore.getInstance("bks");
trustStore.load(null, null);
caCertificate = getX509Certificate("/some/path/ca.crt");
trustStore.setCertificateEntry("ca-cert", caCertificate);
keyStore = KeyStore.getInstance("pkcs12");
keyStore.load(null, null);
InputStream is = new FileInputStream("/some/path/client.p12");
keyStore.load(is, "passwd".toCharArray());
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");
trustManagerFactory.init(trustStore);
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, null);
context = SSLContext.getInstance("TLS");
context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
URL url = new URL("https://www.backend.com");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setSSLSocketFactory(context.getSocketFactory());
connection.setDoInput(true);
connection.setDoInput(true);
BufferedReader urlReader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String inputLine;
while ( (inputLine=urlReader.readLine()) != null ){
System.out.println(inputLine);
}
However, client.p12 is not available at runtime. The http client receives following configuration via a separate
dedicated channel:
- PEM encoded X509 client certificate (client.crt)
- DER formatted private key client (client.der)
Therefore I changed the keystore intitialisation above taking the client.p12 as input, into following:
keyStore = KeyStore.getInstance("bks");
keyStore.load(null, null);
clientCertificate = getX509Certificate("/some/path/client.crt");
byte[] privateKey = getBytesFromFile("/some/path/client.der");
Certificate[] chain = new Certificate[2];
chain[1] = caCertificate;
chain[0] = clientCertificate;
keyStore.setCertificateEntry("client-cert", clientCertificate);
keyStore.setKeyEntry("client-cert-key", privateKey, chain);
runtime an exception is thrown when executing
context = SSLContext.getInstance("TLS");
----> context.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
java.lang.RuntimeException: forget something!
at org.bouncycastle.jce.provider.JDKKeyStore$StoreEntry.getObject(JDKKeyStore.java:314)
at org.bouncycastle.jce.provider.JDKKeyStore.engineGetKey(JDKKeyStore.java:604)
at java.security.KeyStoreSpi.engineGetEntry(KeyStoreSpi.java:376)
at java.security.KeyStore.getEntry(KeyStore.java:734)
at org.apache.harmony.xnet.provider.jsse.KeyManagerImpl.<init>(KeyManagerImpl.java:72)
Summary: everything is working using a pcks12 certificate/privatekey pair, but not using the two in the mentioned format.
Any suggestions what is going wrong or a suggestion for implementing client authentication given the client.der/client.pem mentioned previously?
P.S. running the keytool runtime is not an option, because I don’t have it at runtime and I do not want to go that way.
Simple: passing a key as byte array is not implemented. Quote from
JDKKeyStore.java:You can try to register your key and certificate using the
void setKeyEntry(String alias, Key key, char[] password, Certificate[] chain)method, it seems it is supported (untested).