简体   繁体   中英

SOAP client is not able to authenticate with mutual tls

I have an Apache CXF client that is connecting a SOAP service, and authenticating with mutual TLS. The client fails during the TLS Handshake because the service sends an empty list of client certificates to the server. I am testing this with self-signed certs, and I can prove that my server works with a curl request and with postman. I am pretty sure that the certificates are setup correctly, and I am sure that I am missing a config step in the CXF client.

Here is how I have my client setup

// setting up certs & keystores
String keystore = "client-keystore.jks";
String keystorePassword = "changeit"; // local self-signed certs

String trustStore = "truststore.jks";
String trustStorePassword = "changeit"; // local self-signed certs

// client keystore
KeyStore ks = KeyStore.getInstance("JKS");
ks.load(new FileInputStream(keystore), keystorePassword.toCharArray());

// ca truststore
KeyStore ts = KeyStore.getInstance("JKS");
ts.load(new FileInputStream(trustStore), trustStorePassword.toCharArray());

// key managers
var kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keystorePassword.toCharArray());
KeyManager[] kms = kmf.getKeyManagers();

// trust managers
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ts);
TrustManager[] tms = tmf.getTrustManagers();

TLSClientParameters param = new TLSClientParameters();
param.setSecureSocketProtocol("TLSv1.2");
param.setDisableCNCheck(false);
param.setTrustManagers(tms);
param.setKeyManagers(kms);

// Get the client & setup the tls parameters
BindingProvider bp = (BindingProvider) port;
var client = ClientProxy.getClient(bp);

HTTPConduit https = (HTTPConduit)client.getConduit();
https.setTlsClientParameters(param);

Here is how I created the certificates. My java version is azul zulu openjdk 11.

# Create the CA Authority that both the client and server can trust
openssl req -new -x509 -nodes -days 365 -subj '/CN=my-ca' -keyout ca.key -out ca.crt
 
# Create the server's key, certificate signing request, and certificate
openssl genrsa -out server.key 2048
openssl req -new -key server.key -subj '/CN=localhost' -out server.csr
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out server.crt
 
# Create the client's key, certificate signing request, and certificate
openssl genrsa -out client.key 2048
openssl req -new -key client.key -subj '/CN=my-client' -out client.csr
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -days 365 -out client.crt
openssl x509 --in client.crt -text --noout
 
# Create the root truststore
keytool -import -alias my-ca -file ca.crt -keystore truststore.jks
 
# Create pkcs12 file for key and cert chain
openssl pkcs12 -export -name server-tls -in server.crt -inkey server.key -out server.p12
 
# Create JKS for server
keytool -importkeystore -destkeystore server-keystore.jks -srckeystore server.p12 -srcstoretype pkcs12 -alias server-tls

# Create pkcs12 file for key and cert chain
openssl pkcs12 -export -name client-tls -in client.crt -inkey client.key -out client.p12

# Create JKS for client
keytool -importkeystore -destkeystore client-keystore.jks -srckeystore client.p12 -srcstoretype pkcs12 -alias client-tls

I set debugging on with -Djavax.net.debug=ssl,handshake,data for both the server & the client.

When I use the CXF client to issue a request to the server, it initiates the mutual tls handshake, but the server fails with Fatal (BAD_CERTIFICATE): Empty server certificate chain , and the client fails with Fatal (HANDSHAKE_FAILURE): Couldn't kickstart handshaking...readHandshakeRecord , because it does indeed send an empty certificate list right before hand.

Produced client Certificate handshake message (
"Certificates": <empty list>
)

I have tried a number of different things, but I cannot seem to get the client to work.

Update Out of curiosity, I ran the ws-security sample from the CXF repo, and used my ca certificate, client, and server certificates in the sample. That worked, and it is configured through an xml bean. I tried the same thing with my local, and it still fails.

The difference between the demo and my client is that when it looks for a x.509 RSA certificate, it fails for my client, but succeeds in the demo app. I have it configured mostly the same.

javax.net.ssl|ALL|01|main|2021-07-02 14:17:32.039 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-07-02 14:17:32.040 EDT|CertificateMessage.java:1066|Unavailable authentication scheme: ecdsa_secp256r1_sha256
javax.net.ssl|ALL|01|main|2021-07-02 14:17:32.040 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-07-02 14:17:32.040 EDT|CertificateMessage.java:1066|Unavailable authentication scheme: ecdsa_secp384r1_sha384
javax.net.ssl|ALL|01|main|2021-07-02 14:17:32.040 EDT|X509Authentication.java:213|No X.509 cert selected for EC
javax.net.ssl|WARNING|01|main|2021-07-02 14:17:32.040 EDT|CertificateMessage.java:1066|Unavailable authentication scheme: ecdsa_secp521r1_sha512
javax.net.ssl|ALL|01|main|2021-07-02 14:17:32.040 EDT|X509Authentication.java:213|No X.509 cert selected for RSA
javax.net.ssl|WARNING|01|main|2021-07-02 14:17:32.040 EDT|CertificateMessage.java:1066|Unavailable authentication scheme: rsa_pss_rsae_sha256

That last error is not present when using the demo app and instead, it returns back the certificate.

For anyone who stumbles upon this question, here's how I resolved it.

Once I started playing with the CXF demo code, I was able to simplify it to just its bare minimum set of dependencies and configurations. From there I was able to sort out that it was a matter of a missing dependency in my project.

For starters, we use dropwizard for the server, and we have a dependency on dropwizard-jaxws which brings in the cxf dependencies. I found by whittling away all of the layers, that the demo app only works if cxf-rt-transports-http-jetty is in the list of dependencies.

The transitive dependencies that dropwizard-jaxws include are:

cxf-rt-frontend-jaxws
cxf-rt-transports-http

I also had a dependency on all of dropwizard-core in my client which may have implemented some SPI interface that cxf-rt-transports-http-jetty implements (conjecture). Once I simplified the dependencies and included the one missing dependency, I have a repeatable, working solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM