2012-06-18 145 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

相关问题