Java证书客户端SSL:无法找到所请求目标的有效证书路径

我们需要客户端身份validation才能向我们的某些Web服务发送RESTful请求。 我已经通过密钥工具在我的本地mac os上安装了客户端证书(.pem)和密钥。 这些都不是自签名的。

openssl pkcs12 -export -name myservercert -in not_self_signed.crt -inkey server.key -out keystore.p12 

…并转换为JKS格式

 keytool -importkeystore -destkeystore mykeystore.jks -srckeystore keystore.p12 -srcstoretype pkcs12 -alias myservercert 

我正在尝试构建一个Java客户端来进行身份validation。 这是我到目前为止所提出的:

 import java.io.FileInputStream; import java.security.KeyStore; import javax.net.ssl.SSLContext; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; public class TestClientCustomSSL { public final static void main(String[] args) throws Exception { KeyStore keyStore = KeyStore.getInstance("JKS"); keyStore.load(new FileInputStream("/Users/me/mykeystore.jks"), "mypassword".toCharArray()); SSLContext sslContext = SSLContexts.custom().loadKeyMaterial(keyStore, "mypassword".toCharArray()).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslContext, new String[] {"TLSv1"}, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); try { HttpGet httpget = new HttpGet("https://restful-service-i-am-calling/v1/endpoint/data?ip=0.0.0.1"); System.out.println("Executing request " + httpget.getRequestLine()); CloseableHttpResponse response = httpclient.execute(httpget); try { HttpEntity entity = response.getEntity(); System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); EntityUtils.consume(entity); } finally { response.close(); } } finally { httpclient.close(); } } } 

下面是我收到的堆栈跟踪。 但根据我在这里读到的内容,我的class级应该可以发送请求。

 Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1439) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:209) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:878) at sun.security.ssl.Handshaker.process_record(Handshaker.java:814) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:134) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:107) at com.mycompany.main(ClientCustomSSL.java:101) Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:126) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1421) ... 20 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ... 26 more 

任何指针都表示赞赏。

编辑:::

仅供参考我可以使用与使用wget添加到信任存储区的相同pem和密钥从服务器获得200响应。

 wget --certificate ~/Desktop/my.cert.pem --private-key ~/Desktop/my.key.key https://mycompany.com/v1/939044?data=0.0.0.1 

编辑2 ::: *

根据下面的@EJP答案,还从服务器站点添加了证书:

 openssl x509 -in /dev/null) -out ~/Desktop/the.api.i.am.calling.crt 

…然后我将证书导入到同一个密钥库:

 keytool -importcert -file ~/Desktop/the.api.i.am.calling.crt -alias the.api.i.am.calling.com -keystore /Users/me/mykeystore.jks -storepass mypassword 

运行list命令显示两个证书都在密钥库中:

 keytool -list -keystore /Users/me/mykeystore.jks Enter keystore password: ********* Keystore type: JKS Keystore provider: SUN Your keystore contains 2 entries my.auth.client.cert.com, Oct 17, 2015, PrivateKeyEntry, Certificate fingerprint (SHA1): 3D:95:32:E5:F9:9E:4A:53:84:EB:AB:1B:B9:A2:4C:A5:1B:5E:DA:76 the.api.i.am.calling.com, Oct 18, 2015, trustedCertEntry, Certificate fingerprint (SHA1): 7C:4A:7B:CE:9B:0B:92:C0:4F:C0:DA:84:CF:F2:24:CF:99:83:0B:3F 

但我仍然收到同样的错误。

编辑3 :::

还有一件事我忘了提。 我给服务器端团队的唯一一件事是我们的客户端证书名称……就像dev.auth.client.com。 我是否真的必须将服务器端证书存储在密钥库中?

它对您的客户证书没有任何帮助。 您的信任库不信任服务器证书。

  1. 使用openssl生成P12文件

    openssl pkcs12 -export -in /Users/me/test.authclient.int.com.crt -inkey /Users/me/test.authclient.int.com.key -out authClient.p12 -name authClientCert

  2. 生成信任库密钥

    keytool -genkey -dname“cn = CLIENT”-alias trustStoreKey -keyalg RSA -keystore authClient-truststore.jks -keypass mypassword -storepass mypassword

  3. 现在,导入信任库密钥

    keytool -import -keystore authClient-truststore.jks -file /Users/me/test.authclient.int.com/test.authclient.int.com.crt-alias.test.authclient.int.com

  4. 获取远程证书

    openssl x509 -in <(openssl s_client -connect the.ssl.api.i.want.to.call.com:443 -prexit 2> / dev / null)-out the.api.i.want.to.call。 CRT

  5. 将服务器证书添加到信任库

    keytool -importcert -file the.api.i.want.to.call.crt -alias the.api.i.want.to.call.com -keystore /Users/me/authClient-truststore.jks -storepass mypassword

这是我用来调用需要身份validation的api的客户端。

  KeyStore clientStore = KeyStore.getInstance("PKCS12"); clientStore.load(new FileInputStream("/Users/me/authClient.p12"), "mypassword".toCharArray()); KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); kmf.init(clientStore, "mypassword".toCharArray()); KeyManager[] keyManagers = kmf.getKeyManagers(); KeyStore trustStore = KeyStore.getInstance("JKS"); trustStore.load(new FileInputStream("/Users/me/authClient-truststore.jks"), "mypassword".toCharArray()); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(trustStore); TrustManager[] tms = tmf.getTrustManagers(); SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(keyManagers, tms, new SecureRandom()); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); HttpGet httpget = new HttpGet(requestUrl); httpclient.execute(httpget); 

不是吗 如果我能通过扩展来帮助我,请告诉我,但这应该是你所需要的。