1

我有一个 Apache CXF 客户端,它连接 SOAP 服务,并使用双向 TLS 进行身份验证。客户端在 TLS 握手期间失败,因为服务向服务器发送了一个空的客户端证书列表。我正在使用自签名证书对此进行测试,并且可以证明我的服务器可以使用 curl 请求和邮递员。我很确定证书设置正确,并且我确定我缺少 CXF 客户端中的配置步骤。

这是我的客户端设置方式

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

这是我创建证书的方式。我的 java 版本是 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

-Djavax.net.debug=ssl,handshake,data我为服务器和客户端设置了调试。

当我使用 CXF 客户端向服务器发出请求时,它会启动相互 tls 握手,但服务器失败Fatal (BAD_CERTIFICATE): Empty server certificate chain,客户端失败Fatal (HANDSHAKE_FAILURE): Couldn't kickstart handshaking...readHandshakeRecord,因为它确实事先发送了一个空证书列表。

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

我尝试了许多不同的东西,但我似乎无法让客户工作。

更新 出于好奇,我从 CXF 存储库运行了ws-security 示例,并在示例中使用了我的 ca 证书、客户端和服务器证书。那行得通,它是通过 xml bean 配置的。我在本地尝试了同样的事情,但仍然失败。

演示和我的客户端之间的区别在于,当它查找 x.509 RSA 证书时,它对我的​​客户端失败,但在演示应用程序中成功。我的配置基本相同。

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

使用演示应用程序时不存在最后一个错误,而是返回证书。

4

1 回答 1

1

对于任何偶然发现这个问题的人,这就是我解决它的方法。

一旦我开始使用 CXF 演示代码,我就能够将其简化为最简单的依赖项和配置集。从那里我能够弄清楚这是我的项目中缺少依赖项的问题。

对于初学者,我们使用 dropwizard 作为服务器,并且我们依赖于 dropwizard-jaxws,它引入了 cxf 依赖项。通过削减所有层,我发现演示应用程序仅cxf-rt-transports-http-jetty在依赖项列表中时才有效。

dropwizard-jaxws 包含的传递依赖项包括:

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

我还依赖于我的客户端中的所有 dropwizard-core,它可能已经实现了一些cxf-rt-transports-http-jetty实现(猜想)的 SPI 接口。一旦我简化了依赖关系并包含了一个缺失的依赖关系,我就有了一个可重复的、有效的解决方案。

于 2021-07-09T23:01:19.583 回答