有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何使用Apache HttpClient处理无效的SSL证书?

我知道,关于这个问题有很多不同的问题和答案。。。但我不明白

我有:ubuntu-9.10-desktop-amd64+NetBeans6。7.1从关闭位置按“原样”安装。代表。 我需要通过HTTPS连接到某个网站。为此,我使用Apache的HttpClient

我从教程中读到:

“一旦正确安装了JSSE,通过SSL的安全HTTP通信应该如下所示
简单的HTTP通信。“举个例子:

HttpClient httpclient = new HttpClient();
GetMethod httpget = new GetMethod("https://www.verisign.com/"); 
try { 
  httpclient.executeMethod(httpget);
  System.out.println(httpget.getStatusLine());
} finally {
  httpget.releaseConnection();
}

现在,我写下:

HttpClient client = new HttpClient();

HttpMethod get = new GetMethod("https://mms.nw.ru");
//get.setDoAuthentication(true);

try {
    int status = client.executeMethod(get);
    System.out.println(status);

    BufferedInputStream is = new BufferedInputStream(get.getResponseBodyAsStream());
    int r=0;byte[] buf = new byte[10];
    while((r = is.read(buf)) > 0) {
        System.out.write(buf,0,r);
    }

} catch(Exception ex) {
    ex.printStackTrace();
}

因此,我有一系列错误:

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:1627)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:204)
        at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:198)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:994)
        at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:142)
        at sun.security.ssl.Handshaker.processLoop(Handshaker.java:533)
        at sun.security.ssl.Handshaker.process_record(Handshaker.java:471)
        at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:904)
        at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1132)
        at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:643)
        at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:78)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:828)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:2116)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:1096)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:398)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:171)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:397)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:323)
        at simpleapachehttp.Main.main(Main.java:41)
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:302)
        at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:205)
        at sun.security.validator.Validator.validate(Validator.java:235)
        at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:147)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:230)
        at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:270)
        at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:973)
        ... 17 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:191)
        at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
        at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:297)
        ... 23 more

要创建最简单的SSL连接,我必须做些什么? (可能没有KeyManager和Trust manager等)


共 (4) 个答案

  1. # 1 楼答案

    http://hc.apache.org/httpclient-3.x/sslguide.html

    Protocol.registerProtocol("https", 
    new Protocol("https", new MySSLSocketFactory(), 443));
    HttpClient httpclient = new HttpClient();
    GetMethod httpget = new GetMethod("https://www.whatever.com/");
    try {
      httpclient.executeMethod(httpget);
          System.out.println(httpget.getStatusLine());
    } finally {
          httpget.releaseConnection();
    }
    

    这里可以找到MySSLSocketFactory示例here。它引用了一个^ {CD1}},您可以修改它来信任所有的东西(尽管您必须考虑这一点)

  2. # 2 楼答案

    https://mms.nw.ru使用默认信任管理器集中没有的自签名证书。要解决此问题,请执行以下操作之一:

    • 使用接受任何证书的TrustManager配置SSLContext(见下文)
    • 使用包含证书的适当信任存储来配置SSLContext
    • 将该站点的证书添加到默认Java信任存储中

    下面是一个程序,它创建了一个(几乎毫无价值的)SSL上下文,可以接受任何证书:

    import java.net.URL;
    import java.security.SecureRandom;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.HttpsURLConnection;
    import javax.net.ssl.KeyManager;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    public class SSLTest {
        
        public static void main(String [] args) throws Exception {
            // configure the SSLContext with a TrustManager
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
            SSLContext.setDefault(ctx);
    
            URL url = new URL("https://mms.nw.ru");
            HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
            conn.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
            });
            System.out.println(conn.getResponseCode());
            conn.disconnect();
        }
        
        private static class DefaultTrustManager implements X509TrustManager {
    
            @Override
            public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
    
            @Override
            public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {}
    
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return null;
            }
        }
    }
    
  3. # 3 楼答案

    Apache HttpClient 4.5的方式:

    org.apache.http.ssl.SSLContextBuilder sslContextBuilder = SSLContextBuilder.create();
    sslContextBuilder.loadTrustMaterial(new org.apache.http.conn.ssl.TrustSelfSignedStrategy());
    SSLContext sslContext = sslContextBuilder.build();
    org.apache.http.conn.ssl.SSLConnectionSocketFactory sslSocketFactory =
            new SSLConnectionSocketFactory(sslContext, new org.apache.http.conn.ssl.DefaultHostnameVerifier());
    
    HttpClientBuilder httpClientBuilder = HttpClients.custom().setSSLSocketFactory(sslSocketFactory);
    httpClient = httpClientBuilder.build();
    

    注意:org.apache.http.conn.ssl.SSLContextBuilder不推荐的org.apache.http.ssl.SSLContextBuilder是新的(注意后者的包名中缺少conn

  4. # 4 楼答案

    https://mms.nw.ru可能使用的证书不是由证书颁发机构颁发的。因此,您需要将证书添加到受信任的Java密钥存储中,如unable to find valid certification path to requested target中所述:

    When working on a client that works with an SSL enabled server running in https protocol, you could get error 'unable to find valid certification path to requested target' if the server certificate is not issued by certification authority, but a self signed or issued by a private CMS.

    Don't panic. All you need to do is to add the server certificate to your trusted Java key store if your client is written in Java. You might be wondering how as if you can not access the machine where the server is installed. There is a simple program can help you. Please download the Java program and run

    % java InstallCert _web_site_hostname_
    

    This program opened a connection to the specified host and started an SSL handshake. It printed the exception stack trace of the error that occured and shows you the certificates used by the server. Now it prompts you add the certificate to your trusted KeyStore.

    If you've changed your mind, enter 'q'. If you really want to add the certificate, enter '1', or other numbers to add other certificates, even a CA certificate, but you usually don't want to do that. Once you have made your choice, the program will display the complete certificate and then added it to a Java KeyStore named 'jssecacerts' in the current directory.

    To use it in your program, either configure JSSE to use it as its trust store or copy it into your $JAVA_HOME/jre/lib/security directory. If you want all Java applications to recognize the certificate as trusted and not just JSSE, you could also overwrite the cacerts file in that directory.

    After all that, JSSE will be able to complete a handshake with the host, which you can verify by running the program again.

    To get more details, you can check out Leeland's blog No more 'unable to find valid certification path to requested target'