How to handle invalid SSL certificates with Apache

2019-01-01 02:44发布

I know, there are many different questions and so many answers about this problem... But I can't understand...

I have: ubuntu-9.10-desktop-amd64 + NetBeans6.7.1 installed "as is" from off. rep. I need connecting to some site over the HTTPS. For this I use Apache's HttpClient.

From tutorial I read:

"Once you have JSSE correctly installed, secure HTTP communication over SSL should be as
simple as plain HTTP communication." And some example:

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

By now, I write this:

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();
}

As a result I have a set of errors:

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

What have I to do to create simplest SSL connection? (Probably without KeyManager and Trust manager etc. while.)

15条回答
梦寄多情
2楼-- · 2019-01-01 03:24

In addition to Pascal Thivent's correct answer, another way is to save the certificate from Firefox (View Certificate -> Details -> export) or openssl s_client and import it into the trust store.

You should only do this if you have a way to verify that certificate. Failing that, do it the first time you connect, it will at least give you an error if the certificate changes unexpectedly on subsequent connections.

To import it in a trust store, use:

keytool -importcert -keystore truststore.jks -file servercert.pem

By default, the default trust store should be lib/security/cacerts and its password should be changeit, see JSSE Reference guide for details.

If you don't want to allow that certificate globally, but only for these connections, it's possible to create an SSLContext for it:

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore ks = KeyStore.getInstance("JKS");
FileInputStream fis = new FileInputStream("/.../truststore.jks");
ks.load(fis, null);
// or ks.load(fis, "thepassword".toCharArray());
fis.close();

tmf.init(ks);

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), null);

Then, you need to set it up for Apache HTTP Client 3.x by implementing one if its SecureProtocolSocketFactory to use this SSLContext. (There are examples here).

Apache HTTP Client 4.x (apart from the earliest version) has direct support for passing an SSLContext.

查看更多
唯独是你
3楼-- · 2019-01-01 03:24

For a way to easily add hosts you trust at runtime without throwing out all checks, try the code here: http://code.google.com/p/self-signed-cert-trust-manager/.

查看更多
怪性笑人.
4楼-- · 2019-01-01 03:25

For HttpClient, we can do this :

SSLContext ctx = SSLContext.getInstance("TLS");
        ctx.init(new KeyManager[0], new TrustManager[] {new DefaultTrustManager()}, new SecureRandom());
        SSLContext.setDefault(ctx);

        String uri = new StringBuilder("url").toString();

        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String arg0, SSLSession arg1) {
                return true;
            }
        };

        HttpClient client = HttpClientBuilder.create().setSSLContext(ctx)
                .setSSLHostnameVerifier(hostnameVerifier).build()
查看更多
心情的温度
5楼-- · 2019-01-01 03:28

For Apache HttpClient 4.5+ & Java8:

SSLContext sslContext = SSLContexts.custom()
        .loadTrustMaterial((chain, authType) -> true).build();

SSLConnectionSocketFactory sslConnectionSocketFactory =
        new SSLConnectionSocketFactory(sslContext, new String[]
        {"SSLv2Hello", "SSLv3", "TLSv1","TLSv1.1", "TLSv1.2" }, null,
        NoopHostnameVerifier.INSTANCE);
CloseableHttpClient client = HttpClients.custom()
        .setSSLSocketFactory(sslConnectionSocketFactory)
        .build();

But if your HttpClient use a ConnectionManager for seeking connection, e.g. like this:

 PoolingHttpClientConnectionManager connectionManager = new 
         PoolingHttpClientConnectionManager();

 CloseableHttpClient client = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();

The HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory) has no effect, the problem is not resolved.

Because that the HttpClient use the specified connectionManager for seeking connection and the specified connectionManager haven't register our customized SSLConnectionSocketFactory. To resolve this, should register the The customized SSLConnectionSocketFactory in the connectionManager. The correct code should like this:

PoolingHttpClientConnectionManager connectionManager = new 
    PoolingHttpClientConnectionManager(RegistryBuilder.
                <ConnectionSocketFactory>create()
      .register("http",PlainConnectionSocketFactory.getSocketFactory())
      .register("https", sslConnectionSocketFactory).build());

CloseableHttpClient client = HttpClients.custom()
            .setConnectionManager(connectionManager)
            .build();
查看更多
公子世无双
6楼-- · 2019-01-01 03:29

Once you have a Java Cert Store (by using the great InstallCert class created above), you can get java to use it by passing the "javax.net.ssl.trustStore" param at java startup.

Ex:

java -Djavax.net.ssl.trustStore=/path/to/jssecacerts MyClassName
查看更多
柔情千种
7楼-- · 2019-01-01 03:36

This link explains the requirement you have step by step. If You are not really concerned which certificate you can proceed with the process in below link.

Note You might want to double check what you are doing since, this is a unsafe operation.

查看更多
登录 后发表回答