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

We require client authentication to send a RESTful request to some of our web services. I've installed a client cert (.pem) and key on the my local mac os via the key tool. These are not self signed.

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

...and converted to JKS format

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

I'm trying to build a Java client to do the authentication. Here is what I've come up with so far:

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

}

}

}

Below is the stacktrace that I receive. But based on what I've read here my class should be able to send the request just fine.

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

Any pointers are appreciated.

EDIT:::

FYI I am able to get a 200 response from the server using the same pem and key that I added to the trust store using wget.

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

EDIT 2:::*

Based on @EJP answer below, also added the cert from the server site:

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

...then I imported the cert to the same keystore:

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

Running the list command shows that both certs are in the keystore:

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

But am still receiving the same error.

EDIT 3:::

One more thing I forgot to mention. The only thing I ever gave the server-side team was our client cert names... Ie something like dev.auth.client.com. Do I really have to get the server-side cert to store in the keystore?

解决方案

Use openssl to generate your P12 file

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

Generate the trust store key

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

Now, import the trust store key

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

Get the remote cert

openssl x509 -in /dev/null) -out the.api.i.want.to.call.crt

Add the server cert to the trust store

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

Here's the client that I used to call the api that needed authentication.

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

That't it. Let me know if I can help by expanding, but this should be all you need.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值