2015-10-17 65 views
1

我們需要客戶端身份驗證來向我們的某些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一樣。我真的必須讓服務器端證書存儲在密鑰庫中嗎?

回答

2
  1. 使用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

  2. 生成信任存儲鍵

    密鑰工具-genkey -dname 「CN =客戶」 -alias trustStoreKey -keyalg RSA -keystore authClient-信任。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的X​​509 -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

  5. 服務器證書添加到信任存儲

    密鑰工具-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); 

就是這樣。讓我知道如果我可以通過擴展來提供幫助,但這應該是你所需要的。

+0

我不能讓這個頭或尾巴。步驟1不使用keytool。步驟2生成密鑰* pair *,該密鑰*進入* keystore。*步驟3導入證書,而不是密鑰。客戶端密鑰庫和信任庫都混在一起了。步驟4-5正是你聲稱沒有工作。 – EJP

+0

你難過,我沒有接受你的答案? – Matt

+0

我確信其他人會發現它很有用 – Matt

2

它與您的客戶端證書沒有任何關係。您的信任庫不信任服務器證書。

+0

謝謝EJP。如果你不介意提供一些關於如何讓它信任服務器證書的細節,那將是有幫助的。我在本地運行。 – Matt

+0

真正的問題是爲什麼服務器證書沒有簽名?如果它們都是您的證書,請從服務器KeyStore中導出服務器證書,並使用keytool將其作爲可信證書導入到您的客戶機信任庫中。 – EJP

+0

如果是這樣的話,我可以發送這個嗎?從我的本地工作正常。 wget --certificate〜/ Desktop/my.cert.pem --private-key〜/ Desktop/my.key.key https://mycompany.com/v1/939044?data=0.0.0.1 – Matt

相關問題