You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java OkHttpClient双向认证失败:无客户端证书问题排查

SSL双向认证Java迁移问题:无客户端证书导致认证失败

我正在将一段连接SSL双向认证远程Web服务的PHP代码迁移到Java。服务提供方已提供服务器证书、客户端证书及调用所需私钥。

已执行操作

  • 生成信任库:
    keytool -import -alias WS_SERVER -file WS_SERVER_CERT.pem -keystore truststore.jks -deststorepass somepassword
    
  • 生成客户端密钥库:先将证书转为PKCS12格式,再导入JKS密钥库:
    openssl pkcs12 -export -in CLIENT_CERT.pem -inkey CLIENT_PK.pem -out certificate.p12 -name client_certificate
    
    keytool -importkeystore -srckeystore certificate.p12 -srcstoretype pkcs12 -destkeystore cert.jks
    
  • 使用OkHttpClient编写连接代码:
    SSLFactory sslFactory = SSLFactory.builder()
            .withIdentityMaterial(Path.of("src/main/resources/cert/cert.jks"), "somepassword".toCharArray())
            .withTrustMaterial(Path.of("src/main/resources/cert/truststore.jks"), "somepassword".toCharArray())
            .build();
    
    OkHttpClient httpClient = new OkHttpClient.Builder()
            .sslSocketFactory(sslFactory.getSslSocketFactory(), sslFactory.getTrustManager().orElseThrow())
            .hostnameVerifier(sslFactory.getHostnameVerifier())
            .build();
    
    final String payload = Files.readString(Path.of("src/test/resources/payload.xml"));
    
    String wsUrl = "https://server.ws";
    
    final Request request = new Request.Builder()
            .url(wsUrl)
            .post(RequestBody.create(payload, MediaType.parse("text/xml")))
            .build();
    
    final Call call = httpClient.newCall(request);
    
    final Response response = call.execute();
    
    System.out.println("Response: " + response.body().string());
    

问题现象

上述代码执行后出现认证错误,但使用curl命令可正常连接服务:

curl --cert CLIENT_CERT.pem --key CLIENT_CERT_PK.pem --cacert WS_SERVER_CERT.pem -H "Content-type: text/xml" https://server.ws -d 'some payload'

后续尝试

  • 使用sslcontext-kickstart库直接加载PEM文件,仍失败:
    X509ExtendedKeyManager keyManager = PemUtils.loadIdentityMaterial(Paths.get("src/main/resources/cert/CLIENT_CERT.pem"), Paths.get("src/main/resources/cert/CLIENT_PK.pem"));
    X509ExtendedTrustManager trustManager = PemUtils.loadTrustMaterial(Paths.get("src/main/resources/cert/SERVER_CERT.pem"));
    SSLFactory sslFactory = SSLFactory.builder()
            .withIdentityMaterial(keyManager)
            .withTrustMaterial(trustManager)
            .build();
    
    ConnectionSpec requireTls12 = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
            .tlsVersions(TlsVersion.TLS_1_2)
            .build();
    
    OkHttpClient httpClient = new OkHttpClient.Builder()
            .sslSocketFactory(sslFactory.getSslSocketFactory(), sslFactory.getTrustManager().orElseThrow())
            .connectionSpecs(Arrays.asList(requireTls12))
            .hostnameVerifier(sslFactory.getHostnameVerifier())
            .build();
    
  • 强制Java使用与curl相同的TLS1.3及加密套件,问题依旧;
  • 开启javax.net.debug=all调试,日志提示:No X.509 certificate for client authentication, use empty Certificate message instead,即Java未找到用于客户端认证的X.509证书,发送了空证书消息。

请问该流程中哪里出现问题?为何会出现无客户端证书的提示?


内容的提问来源于stack exchange,提问作者Nik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 07:24:58