2012-06-18 146 views
6

想到我會遇到同樣的問題,我已經經歷了許多類似的問題和潛在的解決方案,但沒有運氣。Java SSL - InstallCert可以識別證書,但仍然「無法找到有效的證書路徑」錯誤?

我使用的信任存儲區是cacerts,位於Java 1.6.0 JRE的lib/security中(構建1.6.0_20-b02 ......這可能是問題的根源?)。我也嘗試過jssecacerts。

使用InstallCert(根據發佈的其他類似問題),我可以看到我的證書實際上已安裝且有效(並且我已將其刪除,重新導入它等等,以確保我看到正確的數據) :

java InstallCert <my host name> 
Loading KeyStore jssecacerts... 
Opening connection to <my host name>:443... 
Starting SSL handshake... 
No errors, certificate is already trusted 

檢查在密鑰工具和Portecle,重新導入證書(我試過從-showcert OpenSSL的生成,從瀏覽器導出和scp'ing過來,等)給我「已經下存在這個別名在這裏「的消息類型。因此,證書進入工具的方式似乎沒有任何問題。

在代碼中強制顯式信任存儲路徑沒有任何區別,並且在所有情況下,當我打開調試(通過javax.net.debug的setProperty爲「all」)時,最終會看到的是:

main, SEND TLSv1 ALERT: fatal, description = certificate_unknown 
main, WRITE: TLSv1 Alert, length = 2 [Raw write]: length = 7 0000: 15 
03 01 00 02 02 2E        ....... main, called 
closeSocket() main, handling exception: 
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 

不幸的是,我不能允許通過實現我自己的TrustManager來覆蓋檢查 - 它必須實際檢查。

我從主機獲得的證書有很多擴展名(準確地說是9個),這讓我想知道他們是不是該問題的一部分。

我還可以檢查/嘗試什麼?切換到不同的JRE版本?

+0

爲什麼你不能實現自己的'TrustManager'?您仍然可以在信任管理器中使用證書並執行檢查。 –

+0

@Vivin - 我試圖解決的核心問題是在我不直接控制的應用程序層(在這種情況下,一個Grails應用程序調用一些最近成爲HTTPS的Web服務),所以雖然我可以使用它在我自己的本地情況下,我無法在上游強制使用它。這是一種痛苦,但稍後我會處理它 - 現在,如果我能夠使它在最細粒度的層面上工作,它至少會告訴我在解決方案鏈中下一步該去哪裏。 – Bill

+0

你是否在像GlassFish這樣的應用服務器上運行它?您可能會檢查該進程是在/usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64中運行Java二進制文件,而不是其他一些不喜歡您的trustStore的Java二進制文件(OpenJDK,等等)。你發佈的調試輸出是有用的,你可以發佈更多? – Brad

回答

0

您仍然可以通過實施自己的信任管理器來檢查證書。我碰到類似的問題here。我也嘗試將證書添加到cacerts,但無濟於事。

在您的信任管理器中,您需要明確加載證書。從本質上講是我必須做的是這樣的:

首先,我創建一個使用實際的證書文件的信託經理:

public class ValicertX509TrustManager implements X509TrustManager { 

    X509TrustManager pkixTrustManager; 

    ValicertX509TrustManager() throws Exception { 

     String valicertFile = "/certificates/ValicertRSAPublicRootCAv1.cer"; 
     String commwebDRFile = "/certificates/DR_10570.migs.mastercard.com.au.crt"; 
     String commwebPRODFile = "/certificates/PROD_10549.migs.mastercard.com.au.new.crt"; 

     Certificate valicert = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(valicertFile)); 
     Certificate commwebDR = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebDRFile)); 
     Certificate commwebPROD = CertificateFactory.getInstance("X509").generateCertificate(this.getClass().getResourceAsStream(commwebPRODFile)); 

     KeyStore keyStore = KeyStore.getInstance("JKS"); 
     keyStore.load(null, "".toCharArray()); 
     keyStore.setCertificateEntry("valicert", valicert); 
     keyStore.setCertificateEntry("commwebDR", commwebDR); 
     keyStore.setCertificateEntry("commwebPROD", commwebPROD); 

     TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("PKIX"); 
     trustManagerFactory.init(keyStore); 

     TrustManager trustManagers[] = trustManagerFactory.getTrustManagers(); 

     for(TrustManager trustManager : trustManagers) { 
      if(trustManager instanceof X509TrustManager) { 
       pkixTrustManager = (X509TrustManager) trustManager; 
       return; 
      } 
     } 

     throw new Exception("Couldn't initialize"); 
    } 

    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
     pkixTrustManager.checkServerTrusted(chain, authType); 
    } 

    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { 
     pkixTrustManager.checkServerTrusted(chain, authType); 
    } 

    public X509Certificate[] getAcceptedIssuers() { 
     return pkixTrustManager.getAcceptedIssuers(); 
    } 
} 

現在,使用這種信任的經理,我必須創建一個套接字工廠:

public class ValicertSSLProtocolSocketFactory implements ProtocolSocketFactory { 

    private SSLContext sslContext = null; 

    public ValicertSSLProtocolSocketFactory() { 
     super(); 
    } 

    private static SSLContext createValicertSSLContext() { 
     try { 
      ValicertX509TrustManager valicertX509TrustManager = new ValicertX509TrustManager(); 
      SSLContext context = SSLContext.getInstance("TLS"); 
      context.init(null, new ValicertX509TrustManager[] { valicertX509TrustManager}, null); 
      return context; 
     } 

     catch(Exception e) { 
      Log.error(Log.Context.Net, e); 
      return null; 
     } 
    } 

    private SSLContext getSSLContext() { 
     if(this.sslContext == null) { 
      this.sslContext = createValicertSSLContext(); 
     } 

     return this.sslContext; 
    } 

    public Socket createSocket(String host, int port, InetAddress clientHost, int clientPort) throws IOException { 
     return getSSLContext().getSocketFactory().createSocket(host, port, clientHost, clientPort); 
    } 

    public Socket createSocket(final String host, final int port, final InetAddress localAddress, final int localPort, final HttpConnectionParams params) throws IOException { 
     if(params == null) { 
      throw new IllegalArgumentException("Parameters may not be null"); 
     } 

     int timeout = params.getConnectionTimeout(); 
     SocketFactory socketFactory = getSSLContext().getSocketFactory(); 

     if(timeout == 0) { 
      return socketFactory.createSocket(host, port, localAddress, localPort); 
     } 

     else { 
      Socket socket = socketFactory.createSocket(); 
      SocketAddress localAddr = new InetSocketAddress(localAddress, localPort); 
      SocketAddress remoteAddr = new InetSocketAddress(host, port); 
      socket.bind(localAddr); 
      socket.connect(remoteAddr, timeout); 
      return socket; 
     } 
    } 

    public Socket createSocket(String host, int port) throws IOException { 
     return getSSLContext().getSocketFactory().createSocket(host, port); 
    } 

    public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { 
     return getSSLContext().getSocketFactory().createSocket(socket, host, port, autoClose); 
    } 

    public boolean equals(Object obj) { 
     return ((obj != null) && obj.getClass().equals(ValicertSSLProtocolSocketFactory.class)); 
    } 

    public int hashCode() { 
     return ValicertSSLProtocolSocketFactory.class.hashCode(); 
    } 
} 

然後我就註冊了一個新的協議:

Protocol.registerProtocol("vhttps", new Protocol("vhttps", new ValicertSSLProtocolSocketFactory(), 443)); 
PostMethod postMethod = new PostMethod(url); 
for (Map.Entry<String, String> entry : params.entrySet()) { 
    postMethod.addParameter(entry.getKey(), StringUtils.Nz(entry.getValue())); 
} 

HttpClient client = new HttpClient(); 
int status = client.executeMethod(postMethod); 
if (status == 200) { 
    StringBuilder resultBuffer = new StringBuilder(); 
    resultBuffer.append(postMethod.getResponseBodyAsString()); 
    return new HttpResponse(resultBuffer.toString(), ""); 
} else { 
    throw new IOException("Invalid response code: " + status); 
} 

唯一缺點是我必須爲這個特定的證書創建一個特定的協議(vhttps)。

+0

很難相信所有真正必要的。沒有理解這一切的根本問題有一種獨特的風格。 – EJP

+0

@EJP無需如此光顧。我很痛苦地意識到這個解決方案並不是最優的。如果你有更好的解決方案,我很高興聽到它。我嘗試了很多事情來實現這一點,包括將證書添加到服務器的cacerts文件中(我甚至確保我可以一直跟蹤信任鏈)。似乎沒有任何工作。在最終使用這個解決方案之前,我研究了這個問題。這不是我的第一選擇。 –

+0

@Vivin - 欣賞這個建議,儘管我真的希望我不必走這條路。 :)其他許多我不直接控制的代碼都必須更改,所以這是我希望在我的某個地方或機器配置端出現錯誤的情況之一。 – Bill

0

我的猜測或者是這些事情發生了:

a)您在Web服務器上運行代碼。他們經常使用他們自己的信任存儲 - 所以你確定當你的代碼執行時使用的是cacerts

b)默認情況下,Java將嘗試通過下載和解釋CRL來檢查證書的有效性。如果您使用代理服務器,則下載失敗,因此整個PKIX檢查將失敗。

+1

當他直接安裝服務器證書時,(b)不適用。好的答案,否則。 – EJP

+0

@EJP糟糕,你是對的,我把它與導入根證書混合起來。將解決這個問題。 – emboss

+0

起初我以爲A就是這樣,然後當我用完整的調試把它煮成小樣本代碼時,它仍然失敗,我很失望。請欣賞這些建議。 – Bill

0

只要您不手動加載它,SSL調試跟蹤將顯示您正在使用哪個cacerts文件。顯然你並沒有使用你認爲的那個。

+0

好想過檢查,但不是這種情況:「trustStore是:/usr/lib/jvm/java-1.6.0-sun-1.6.0.20.x86_64/jre/lib/security/jssecacerts」。正如我所提到的,我已經用jssecacerts和cacerts對它進行了嘗試,其中包括通過trustStore設置強制執行路徑。 – Bill

相關問題