11

It's not something that should happen by design, but for security concerns, i'm wondering how will the "right" certificate be sent to the server, assuming there are more than one certificates matching the requirement of being signed by a certain CA?

I'm using a simple SSL JAVA example client, connecting to an Apache HTTPD.

I tried testing with 4 certificates, each time deleting the chosen one and noting who was chosen next. I couldn't find a reasonable logic (i.e. date, alias name etc.) other than maybe a lexicographic order of the "sha256" of the certificates. that seems unlikely to me...

The example client does something like

System.setProperty("javax.net.ssl.keyStore","device.p12");  
System.setProperty("javax.net.ssl.keyStorePassword","password");  
System.setProperty("javax.net.ssl.keyStoreType", "PKCS12");  
System.setProperty("javax.net.ssl.trustStore","truststore.jks");  
System.setProperty("javax.net.ssl.trustStorePassword","password");  
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();  
SSLSocket sslSock = (SSLSocket) factory.createSocket("87.69.60.100",443);  
BufferedWriter wr = new BufferedWriter(new OutputStreamWriter(sslSock.getOutputStream(), "UTF8"));  
wr.write("GET /lather HTTP/1.1\r\nhost: 87.69.60.100\r\n\r\n");  
wr.flush();  

And the Apache is configured with

SSLCACertificateFile rootCA.crt  
SSLVerifyClient require  

I couldn't find the relevant documentation to answer the question. I'm also wondering- is there any chance that the Apache will somehow forward more than one certificates chains? (say with a misbehaving client sending something weird).

Thanks!

yair
  • 659
  • 2
  • 8
  • 26
  • It isn't specified. I don't understand the part about 'Apache forwarding', but only one certificate chain will be sent. – user207421 May 07 '14 at 21:15
  • @EJP I wrote forwarded since I configured the Apache to forward the certificate to a Tomcat using "SSLOptions +ExportCertData". You mean it's not specified in the standard **and** in the java implementation? weird... but it does choose something, my only real option is go over their code to find out which one? ("their" being SSLSocket i guess?) – yair May 08 '14 at 04:29
  • Not much point in that. If it isn't specified, you can't rely on what the current code does. It may change tomorrow. – user207421 May 09 '14 at 10:20

1 Answers1

19

A server that requires client authentication will send a list of acceptable certificate types, possibly along with a list of acceptable CAs. By default, your Java client then applies the following algorithm:

  1. For each certificate type (RSA, DSA, EC) accepted by the server, find any public/private key pairs in the key store which have been generated with the specified algorithm
  2. If the server sent a list of acceptable CAs, remove any key pair that doesn't contain any of the CAs in the list in its certificate chain
  3. If there is at least one key pair remaining, select the private key corresponding to the first one; otherwise go back to step 1 for the next key type.

The client certificate choice algorithm isn't specified in RFC 5246, but Java's simple default implementation seems reasonable, if subject to change in the future as noted by EJP. In particular, the 'first' one is pretty much random - credentials are currently stored in a Map, so it is going to depend on iteration order of the entry set. Also, the KeyManager implementations are pluggable, and there is a 'NewSun' implementation available with OpenJDK that is activated by passing the security property ssl.KeyManagerFactory.algorithm=NewSunX509. This second one will also take into account of your client certificates' keyUsage and extendedKeyUsage attributes, as well as the expiry dates.

If you need to guarantee the certificate sent from a list of possibilities and you find that the default behaviours aren't doing it for you, your best option is to manually create a single-entry keystore and use it to initialise an SSLContext, or write your own implemenation of X509KeyManager to do what you want in chooseClientAlias, like in the answers to this question or this question.

Community
  • 1
  • 1
rxg
  • 3,777
  • 22
  • 42
  • The key usage (if present in potential client certs) is also used to make the choice. – Bruno Jun 02 '14 at 00:27
  • Actually Bruno, as of Java 7u60 and 8u5 the default KeyManager implementation doesn't seem to check key usage or extended key usage – rxg Jun 02 '14 at 09:39
  • If you look at what's done with [`CheckType`](https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/sun/security/ssl/X509KeyManagerImpl.java#L694) in `getAliases`, itself called by `chooseAlias`, it looks like certificates with the right EKU will be preferred. – Bruno Jun 02 '14 at 10:03
  • Hmm, they must have changed the algorithm since that dump - with 7u60 I don't get the same result, or the same debug messages. I'm looking at http://www.java.net/download/openjdk/jdk7u40/promoted/b43/openjdk-7u40-fcs-src-b43-26_aug_2013.zip for the source, and if I test with a key with `-ext eku=codeSig -ext ku=cRLSign` it still gets selected – rxg Jun 02 '14 at 10:12
  • Having these extensions doesn't mean it won't get selected, it just means it won't be preferred if there's a better match. Do you test with just that cert or others better matching certs in your keystore too? – Bruno Jun 02 '14 at 10:19