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();