Java HTTPS client fails SSL handshake while curl succeeds

Zik :

Apologies up front, I'm still pretty new to coding for SSL. I've been searching for answers for the past few days, and while I've found a lot of suggestions nothing has worked so far.

What I have is a server implemented on top of Dropwizard that needs to accept an incoming HTTPS connection and use the attached certificate to uniquely identify the client. I am currently using all self-signed certificates while I'm in development. The server certificate pair was created using a chain - root pair -> intermediate pair -> server pair. The server's P12 was created using a concatenation of the intermediate and server certificates plus the server private key. It was then added to an empty JKS and became the server's KeyStore.

Separately I've created two client certificates, one using the same intermediate pair as the base, and another as a pure stand-alone certificate pair. The x509 public key portion of both of these certificate pairs was added to a JKS file and became the server's TrustStore. The Dropwizard configuration follows:

type: "https"
port: "9843"
keyStorePath: "keystore.jks"
keyStorePassword: "changeme"
keyStoreType: "JKS"
trustStorePath: "truststore.jks"
trustStorePassword: "changeme"
trustStoreType: "JKS"
allowRenegotiation: false
validateCerts: false
validatePeers: false
needClientAuth: true
wantClientAuth: true

I can connect to the server using curl and either of the client certificate pairs:

curl -v --cert client.pem --key client.key -k https://localhost:9843/v1/ld 

With SSL debug turned on, the server logs the following:

*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Cert Authorities:
<CN=*.me.com, O=Me, ST=Massachusetts, C=US>
<O=Internet Widgits Pty Ltd, ST=Massachusetts, C=US>
*** ServerHelloDone
dw-51, WRITE: TLSv1.2 Handshake, length = 3536
dw-44, READ: TLSv1.2 Handshake, length = 1047
*** Certificate chain
chain [0] = [
[
  Version: V3
  Subject: O=Internet Widgits Pty Ltd, L=North Reading, ST=Massachusetts, C=US
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 2048 bits
  modulus: 23250299629324311533731283912176366463399376328149948822580485256237233115567136794461732268017120297060017586981907979910958857247642884566364833267711927344361604478514119965230314679194017013023991389216461419030751049820266939279047536006291610734616600760688907006770883510297954698233112783686968024400749969025850008781641616624298935923926427096257861170476293580684942956111432790304698635393966967864288730561678135798437678912431564767611000006312358137647455886578135011989168265295083928014176435879778838966450081419161406209555593636745048857672445188811541416453143809594265089422302064600885289819601
  public exponent: 65537
  Validity: [From: Wed Dec 05 13:52:49 EST 2018,
               To: Thu Dec 05 13:52:49 EST 2019]
  Issuer: O=Internet Widgits Pty Ltd, ST=Massachusetts, C=US
  SerialNumber: [    8174655c c8387da4]

Certificate Extensions: 3
...

So far so good. Next I try to connect to my server using a Java client using the same certificate pair, combined in a single P12 file. Java code follows:

char[] password = "changeme".toCharArray();
KeyStore keystore = KeyStore.getInstance("PKCS12");
try (FileInputStream fileInputStream = new FileInputStream("client.p12")) {
    keystore.load(fileInputStream, password);
}

SSLContext sslContext = 
        SSLContexts.custom()
                   .loadKeyMaterial(keystore, password)
                   .loadTrustMaterial(null, (chain, authType) -> true)
                   .build();

return HttpClients.custom()
                  .setSSLContext(sslContext)
                  .setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
                  .build();

But when this client tries to connect the server logs the following:

*** CertificateRequest
Cert Types: RSA, DSS, ECDSA
Supported Signature Algorithms: SHA512withECDSA, SHA512withRSA, SHA384withECDSA, SHA384withRSA, SHA256withECDSA, SHA256withRSA, SHA256withDSA, SHA224withECDSA, SHA224withRSA, SHA224withDSA, SHA1withECDSA, SHA1withRSA, SHA1withDSA
Cert Authorities:
<CN=*.me.com, O=Me, ST=Massachusetts, C=US>
<O=Internet Widgits Pty Ltd, ST=Massachusetts, C=US>
*** ServerHelloDone
Warning: no suitable certificate found - continuing without client authentication
*** Certificate chain
<Empty>
***

I have also tried using a SSLConnectionSocketFactory initialized with the sslContext, and a Registry<ConnectionSocketFactory> registering the socketFactory for "https". Nothing has worked. I am totally at a loss as to why curl accepts the cert authorities and sends the client certificate but the java httpClient does not.

EDIT:

I tried adding the server's public certificate to the client request, it did not make a difference - I'm still seeing the same behavior. The code was updated as follows:

KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
try (FileInputStream fileInputStream = new FileInputStream("server.cert.pem")) {
    try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(IOUtils.toByteArray(fileInputStream))) {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X509");
        Certificate certificate = certificateFactory.generateCertificate(byteArrayInputStream);
        trustStore.setCertificateEntry("server", certificate);
    }
}

SSLContext sslContext =
        SSLContexts.custom()
                   .loadKeyMaterial(keystore, password)
                   .loadTrustMaterial(trustStore, (chain, authType) -> true)
                   .build();
Zik :

So I ended up completely reworking how my certificates were being generated and I was able to get things working, with a caveat: it depended how the certificates were generated.

Works with Java client, curl, and Postman:

openssl genrsa -aes256 -out private/${SVRNAME}.key.pem 2048
openssl req -config ${CONFIGDIR}/openssl.cnf \
      -new -x509 -days 7300 -sha256 -extensions v3_ca \
      -key private/${SVRNAME}.key.pem \
      -out certs/${SVRNAME}.cert.pem

Works with curl and Postman but not Java client:

openssl req -newkey rsa:2048 -nodes \
      -keyout private/${CLINAME}.key.pem \
      -x509 -days 365 -out certs/${CLINAME}.cert.pem

Not sure why the "quick" certificate caused so many problems but at least it is working now. Thanks to Patrick and Dave for the help!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=131525&siteId=1