代码之家  ›  专栏  ›  技术社区  ›  Vivin Paliath

建立SSL连接时pkix路径生成失败

  •  16
  • Vivin Paliath  · 技术社区  · 15 年前

    我正在与一个名为commweb的商人帐户集成,我正在向他们的url发送一个ssl邮件。( https://migs.mastercard.com.au/vpcdps )当我试图发送邮件时,我得到以下异常:

    sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    

    执行POST的代码(我没有编写,并且已经存在于我们的代码库中)是:

    public static HttpResponse sendHttpPostSSL(String url, Map<String, String> params) throws IOException {
        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);
        }
    }
    

    商业帐户集成的文档没有提到证书。他们提供了一些似乎盲目接受证书的示例JSP代码:

    <%! // Define Static Constants
        // ***********************
    public static X509TrustManager s_x509TrustManager = null;
    public static SSLSocketFactory s_sslSocketFactory = null;
    
    static {
            s_x509TrustManager = new X509TrustManager() {
            public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[] {}; } 
            public boolean isClientTrusted(X509Certificate[] chain) { return true; } 
            public boolean isServerTrusted(X509Certificate[] chain) { return true; } 
        };
    
        java.security.Security.addProvider(new com.sun.net.ssl.internal.ssl.Provider());
        try {
            SSLContext context = SSLContext.getInstance("TLS");
            context.init(null, new X509TrustManager[] { s_x509TrustManager }, null);
            s_sslSocketFactory = context.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e.getMessage());
        }
    }
    
    ...
    ...
               // write output to VPC
                SSLSocket ssl = (SSLSocket)s_sslSocketFactory.createSocket(s, vpc_Host, vpc_Port, true);
                ssl.startHandshake();
                os = ssl.getOutputStream();
                // get response data from VPC
                is = ssl.getInputStream();
    ...
    ...
    %>
    

    我们的webapp有一个密钥库,我尝试使用 keytool 命令,但没用,我也犯了同样的错误。我在网上尝试过解决方案(导入密钥并使用 System.setProperty )但那似乎有点笨拙,而且不起作用(给了我一个 NoSuchAlgorithmError )感谢您的帮助!

    2 回复  |  直到 14 年前
        1
  •  13
  •   President James K. Polk    15 年前

    显然,valicert class 3 CA证书不在默认的信任库中(可能是jre lib/security目录中的cacerts文件,但请参见 JSSE documentation 完整的故事)。

    您可以将此证书添加到cacerts文件,但我不推荐这样做。相反,我认为您应该创建自己的信任库文件(可以是cacerts文件的副本),并将valicert根CA添加到此文件中。然后指向此文件 javax.net.ssl.trustStore 系统属性。

        2
  •  7
  •   Vivin Paliath    14 年前

    我想我应该用我实际做的事情来更新这个答案。使用Gregs提供的文档,我为valicert创建了一个信任管理器。在信任管理器中,我加载证书文件:

    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 )对于这个特殊的证书。