2012-07-31 44 views
7

在Windows 7上運行的Java 1.7應用程序中,我嘗試使用服務器執行雙向SSL(a智能卡令牌通過openSC提供我的客戶端證書)。服務器的證書得到了客戶端的驗證,但客戶端不響應服務器的證書請求。我相信這是因爲客戶端無法從我的證書創建鏈條到服務器請求的鏈條(儘管存在這樣的鏈條)。我的SSL客戶端(Java)沒有通過雙向SSL握手將證書發送回服務器

這裏是服務器的證書請求的SSL調試和客戶端空響應:

*** CertificateRequest 
Cert Types: RSA, DSS, ECDSA 
Cert Authorities: 
<CN=c4isuite-SDNI-DC02-CA, DC=c4isuite, DC=local> 
<CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US> 
    ... 
*** ServerHelloDone 
*** Certificate chain 
*** 

我的客戶端證書如下:

found key for : Certificate for PIV Authentication 
chain [0] = [ 
[ 
    Version: V3 
    Subject: CN=<...>, OU=CONTRACTOR, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 

    Key: Sun RSA public key, 2048 bits 

    Issuer: CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    SerialNumber: [ 05bf13] 

通過關鍵的工具,我也裝在truststore(java cacerts文件),我的證書頒發者DOD CA-30與服務器請求的內容之間應該有什麼聯繫,DoD根CA 2.

來自SSL debug:

adding as trusted cert: 
    Subject: CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Algorithm: RSA; Serial number: 0x1b5 
    Valid from Thu Sep 08 10:59:24 CDT 2011 until Fri Sep 08 10:59:24 CDT 2017 

adding as trusted cert: 
    Subject: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Algorithm: RSA; Serial number: 0x5 
    Valid from Mon Dec 13 09:00:10 CST 2004 until Wed Dec 05 09:00:10 CST 2029 

所以問題是,爲什麼客戶端不能爲響應創建證書鏈?下面是相關的代碼:

// Create the keyStore from the SmartCard certs 
    Provider provider = new sun.security.pkcs11.SunPKCS11(configName); 

    Security.addProvider(provider); 
    keyStore = KeyStore.getInstance("PKCS11", "SunPKCS11-SCR3310test"); 
    char[] pin = PIN.toCharArray(); 
    keyStore.load(null, pin); 

     // Init the trustmanager 
     TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 
     tmf.init(trustStore); 

     // Create the client key manager 
     LOG.info("Installing keystore with pin"); 
     KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); 
     keyManagerFactory.init(clientKeyStore, clientKeyPassword.toCharArray());   

     sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null); 

     // Init SSL context 
     SSLSocketFactory socketFactory = sslContext.getSocketFactory(); 


     URL url = new URL(urlString); 
     URLConnection connection = url.openConnection(); 
     if (connection instanceof HttpsURLConnection) { 
      LOG.info("Connection is HTTPS"); 
      ((HttpsURLConnection) connection).setSSLSocketFactory(socketFactory); 
     } 

     // Send the request. 
     connection.connect(); 

     InputStreamReader in = new InputStreamReader((InputStream) connection.getContent()); 
     ... 

而且我回來的錯誤是服務器,因爲客戶端沒有發送客戶端證書返回一個403最有可能的。

回答

3

儘管看起來您只是將服務器發送的CA列表的一部分複製到此問題中,但我認爲CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US不在此列表中。

什麼,似乎在鏈缺失是該證書(你提到以後):

Subject: CN=DOD CA-30, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Issuer: CN=DoD Root CA 2, OU=PKI, OU=DoD, O=U.S. Government, C=US 
    Algorithm: RSA; Serial number: 0x1b5 
    Valid from Thu Sep 08 10:59:24 CDT 2011 until Fri Sep 08 10:59:24 CDT 2017 

證書導入到客戶的信任有客戶端發送的證書上絕對沒有任何影響。客戶端證書(及其私鑰)需要在客戶端密鑰庫中設置。此外,如果您想發送客戶端證書鏈(如果服務器在列表中未提供此中間CA證書),則需要將完整鏈關聯到該證書條目。將其他證書放入密鑰庫並不足夠。

要解決此問題,您應該使用客戶端證書鏈來配置密鑰存儲庫條目。這可以按照this answer中所述完成。然而,這可能是通過PKCS#11訪問硬件令牌的事實可能會使這一點變得更加複雜(也許還有另一種與該卡一起提供的證書管理工具,可能獨立於Java)。

+0

感謝您直接在不使用信任存儲庫的客戶端上找到完整的證書鏈。由於證書來自智能卡,因此修改證書很困難或無法完成。我確實設法以另一種方式繞過它,我在下面詳細說明了答案,以便其他人在相同情況下可能會有答案。 – PaulP 2012-08-01 15:29:40

2

由於我知道哪些證書需要用於服務器身份驗證,因此我可以強制客戶端通過擴展X509ExtendedKeyManager來發送特定證書,並覆蓋chooseClientAlias()方法以始終返回該證書的別名。代碼:

public class MyX509KeyManager extends X509ExtendedKeyManager 
    { 
    X509KeyManager defaultKeyManager; 

    public MyX509KeyManager(X509KeyManager inKeyManager) { 
     defaultKeyManager = inKeyManager; 
    } 

    public String chooseEngineClientAlias(String[] keyType, 
      Principal[] issuers, SSLEngine engine) { 
     return "<Alias of my cert>"; 
    } 

    @Override 
    public String chooseClientAlias(String[] strings, Principal[] prncpls, Socket socket) { 
     return "<Alias of my cert>"; 
    } 

    @Override 
    public String[] getClientAliases(String string, Principal[] prncpls) { 
     return defaultKeyManager.getClientAliases(string, prncpls); 
    } 

    @Override 
    public String[] getServerAliases(String string, Principal[] prncpls) { 
     return defaultKeyManager.getServerAliases(string, prncpls); 
    } 

    ... 

因此,大家可以看到,我參加一個defaultKeyManager我推遲至除我要重寫任何東西。然後,要在你的sslContext中使用它,請執行以下操作:

// clientKeyStore is initialized elsewhere from the SmartCard 
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509"); 
keyManagerFactory.init(clientKeyStore, clientKeyPassword.toCharArray()); 

MyX509KeyManager customKeyManager = new MyX509KeyManager((X509KeyManager) keyManagerFactory.getKeyManagers()[0]); 
sslContext.init(new KeyManager[] {customKeyManager}, tmf.getTrustManagers(), null); 
+1

這有助於客戶端發送此證書,但如果服務器缺少中間證書以構建鏈以驗證它,則幾乎肯定會出現問題。 (這隻能在服務器知道中間證書但不在證書請求消息中發送它的名字的情況下才有效,這是可能的。) – Bruno 2012-08-01 15:57:03

+0

你可以做的也是重寫'getCertificateChain'來把這個證書添加到手動鏈。 – Bruno 2012-08-01 16:18:59

+0

正確,這是假定服務器確實擁有完整的證書鏈。 – PaulP 2012-08-01 18:20:34

相關問題