2

I'm using a Java SE Jersey client to connect to a HTTPS resource which uses two-way SSL.

My SSLContext looks like this:

private SSLContext getSSLContext() {
    SslConfigurator sslConfig = SslConfigurator.newInstance()
        .keyStoreFile("src/main/certificates/testcert.p12")
        .keyPassword("mypassword");

   return sslConfig.createSSLContext();
}

The problem is that the client certificate is never sent.

I get error "Warning: no suitable certificate found - continuing without client authentication" and I've tracked the reason to the fact that the client certificate isn't issued to one of the Cert Authorities listed in the server's CertificateRequest message to the client. I know from testing with cURL that the server will accept the certificate regardless. The endpoint is a public test system.

My question: How do I force-send my client certificate? (i.e. my Java SE client should ignore the fact that the testcert.p12 certificate's issuer is not the list of issuers that the server has said it would accept)

Please, don't point me to answers that are about disabling check of the server's certificate or about using self-signed certificates. The server's certificate is just fine.

UPDATE

It turned out my problem was another one. I debug by setting system property javax.net.debug=all. After examining the resulting output it looked to me as if the keystore was empty, even after doing the above. So no wonder why "no suitable certificate found".

Jersey has this 'clever' SslConfigurator class which is there to help you set up an SSLContext. Perhaps just too clever for me, because I couldn't make it work with the above code. So instead, I now configure my SSLContext like below:

    KeyStore ks = KeyStore.getInstance("PKCS12");
    FileInputStream fis = new FileInputStream("src/main/certificates/testcert.p12");
    ks.load(fis, "mypassword".toCharArray());
    KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
    kmf.init(ks, "mypassword".toCharArray());
    SSLContext sc = SSLContext.getInstance("TLS");
    sc.init(kmf.getKeyManagers(), null, null);
    // now use 'sc' in Jersey

This works for me where Jersey's helper class didn't. I fully sympathize with Jersey's idea of a helper class for SSLContext, because JSSE seems overly complex here for such a simple use case. Well, well.

peterh
  • 18,404
  • 12
  • 87
  • 115
  • I quite agree. Reopened. @feeling_unwelcome This question is not in any way a duplicate of [Ignore self-signed certificate using Jersey client](https://stackoverflow.com/questions/6047996/ignore-self-signed-ssl-cert-using-jersey-client). Don't be so trigger-happy. – user207421 Jun 29 '18 at 06:44

1 Answers1

2

You can't. It would be a TLS protocol violation, and therefore there is no API to support it. The various TLS APIs will only send a client certificate if:

  1. It was requested, and
  2. A client certificate can be found that conforms to what is specified in the CertificateRequest message.

You will have to arrange for your server to trust your client certificate, either by getting it signed by a CA or by exporting it to the server's trusted certificate store, whatever form that takes at the server.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Thanks. Fully makes sense. – peterh Jun 29 '18 at 17:12
  • @user207421 can you say more about "It would be a TLS protocol violation"? I read through RFC 2246 and found nothing stating that a client shall or must not offer a client certificate that isn't in the list of acceptable CA names presented by the server. Both curl and openssl s_client will happily send a client certificate that isn't in the list presented by the server, and in my experience many times servers will accept client certificates that are not issued by a "trusted CA" but have been added to an allow list w/ their SPKI fingerprint or otherwise. – TonyLovesDevOps Jun 02 '22 at 18:10