11

UPDATE: As for my original question, it turns out that call to java.security.KeyStore.getCertificate(alias) does actually return X509Certiciate. That wasn't the issue though.

(Bear with me please, I'm new to this certificate stuff.)

I managed to connect to my (self-signed) SSL-enabled server, as long as I don't require authenticated clients. When I do require clientAuth my app yields "routines:SSL3_READ_BYTES:sslv3 alert handshake failure (external/openssl/ssl/s3_pkt.c"... (also described here)... For some the cure was to switch from BKS to PKCS12, that did not work for me.

So now I am trying to implement my own X509KeyManager (as suggested here), to hand it to sslContext.init([keyManager], trustManagers, null).

If I understand it correctly the sslContext will ask my keyManager(s) for either a certificate chain and/or a private key for a given alias. (Whenever it asks what alias to choose I provide my hard-coded one.)

But according to the X509KeyManager interface I am supposed to return X509Certificate. How do I create one using the keystore?

Community
  • 1
  • 1
Jaroslav Záruba
  • 4,694
  • 5
  • 39
  • 58

1 Answers1

18

You can use a KeyStore with your client certificate for client authentication without explicitly creating a KeyManager. The code should be something like this:

KeyStore keyStore = KeyStore.getInstance("BKS");
InputStream is = getResources().openRawResource(R.raw.client);
keyStore.load(is, "yourKeyStorePassword".toCharArray());
is.close();

KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("X509");
keyManagerFactory.init(keyStore, "yourKeyStorePassword".toCharArray());

SSLContext sslContext = SSLContext.getInstance("TLS"); 
sslContext.init(keyManagerFactory.getKeyManagers(), trustManagers, null);

Also make sure that your server trusts your client certificate.

Tomik
  • 23,857
  • 8
  • 121
  • 100
  • This is how I tried it in the first place (based on "Providing an application specific X509KeyManager" article on Android Developers) but ran into the handshake problem. Which made me think the kmf for some reason fails to hand the proper keystore entry. But your last sentence is something that I never realized: maybe the server does inspect the proper certificate but simply does not like its smell. Given contents of my keystore is not "approved" by any CA it makes sense... I will look into it and mark your response if it is actually the case. :) – Jaroslav Záruba Mar 07 '15 at 23:05
  • 1
    Damn, you were right about the last thing. Added my custom .truststore JKS to Tomcat HTTPS Connector, problem solved. :) Thanks a lot! – Jaroslav Záruba Mar 08 '15 at 00:07
  • 9
    To anyone coming across this answer, be sure to use the import `javax.net.ssl.TrustManagerFactory` and NOT `com.sun.net.ssl.TrustManagerFactory`; the later is now deprecated. Cheers. – Matt Clark Jul 24 '15 at 14:20
  • 2
    I copied/paste your solution but "trustManagers" is no defined – Federico Traiman Aug 28 '20 at 19:22
  • 1
    @FedericoTraiman, passing in null instead of trustManagers works fine in my use case. – nibarius May 14 '22 at 21:25
  • I had to use `KeyManagerFactory.getInstance("SunX509")` on Java 11. – Joe23 Jun 21 '22 at 14:53