Java证书客户端SSL:无法找到要求的目标的有效证书路径
我们需要客户端身份验证来向我们的某些Web服务发送RESTful请求。我已经通过关键工具在我的本地mac os上安装了客户端证书(.pem)和密钥。这些不是自签名的。Java证书客户端SSL:无法找到要求的目标的有效证书路径
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客户端做认证。这是我到目前为止:
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();
}
}
}
下面是我收到的堆栈跟踪。但根据我读的here我的班级应该能够发送请求就好了。
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
任何指针的赞赏。
编辑:::
FYI我能够得到使用相同的PEM和关键,我使用wget添加到信任存储服务器200响应。
wget --certificate ~/Desktop/my.cert.pem --private-key ~/Desktop/my.key.key https://mycompany.com/v1/939044?data=0.0.0.1
EDIT 2 ::: *
基于@EJP答案下面,还增加了从服务器站点的证书:
openssl x509 -in <(openssl s_client -connect the.api.i.am.calling.com:443 -prexit 2>/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一样。我真的必须让服务器端证书存储在密钥库中吗?
-
使用OpenSSL生成您的P12文件
OpenSSL的PKCS12 -export -in /Users/me/test.authclient.int.com.crt -inkey /Users/me/test.authclient.int .com.key退房手续authClient.p12 -name authClientCert
-
生成信任存储键
密钥工具-genkey -dname “CN =客户” -alias trustStoreKey -keyalg RSA -keystore authClient-信任。JKS -keypass输入mypassword -storepass输入mypassword
-
现在,导入信任存储键
的keytool -import -keystore authClient-truststore.jks -file /Users/me/test.authclient.int.com/test。 authclient.int.com.crt -alias.test.authclient.int.com
-
获取远程证书
OpenSSL的X509 -in <(OpenSSL的的s_client.First -connect the.ssl.api.i. want.to.call.com:443 -prexit 2>/dev/null)-out the.api.i.want.to.call.crt
-
服务器证书添加到信任存储
密钥工具-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
下面是我用来调用需要认证的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);
就是这样。让我知道如果我可以通过扩展来提供帮助,但这应该是你所需要的。
它与您的客户端证书没有任何关系。您的信任库不信任服务器证书。
谢谢EJP。如果你不介意提供一些关于如何让它信任服务器证书的细节,那将是有帮助的。我在本地运行。 – Matt
真正的问题是为什么服务器证书没有签名?如果它们都是您的证书,请从服务器KeyStore中导出服务器证书,并使用keytool将其作为可信证书导入到您的客户机信任库中。 – EJP
如果是这样的话,我可以发送这个吗?从我的本地工作正常。 wget --certificate〜/ Desktop/my.cert.pem --private-key〜/ Desktop/my.key.key https://mycompany.com/v1/939044?data=0.0.0.1 – Matt
我不能让这个头或尾巴。步骤1不使用keytool。步骤2生成密钥* pair *,该密钥*进入* keystore。*步骤3导入证书,而不是密钥。客户端密钥库和信任库都混在一起了。步骤4-5正是你声称没有工作。 – EJP
你难过,我没有接受你的答案? – Matt
我确信其他人会发现它很有用 – Matt