使用Java忽略SSL证书错误

10

Apache Http Client。您可以在这里查看相关代码:

String url = "https://path/to/url/service";
HttpClient client = new HttpClient();
PostMethod method = new PostMethod(url);

// Test whether to ignore cert errors
if (ignoreCertErrors){
  TrustManager[] trustAllCerts = new TrustManager[]{
    new X509TrustManager(){
      public X509Certificate[] getAcceptedIssuers(){ return null; }
      public void checkClientTrusted(X509Certificate[] certs, String authType) {}
      public void checkServerTrusted(X509Certificate[] certs, String authType) {}
    }
  };

  try {
    SSLContext sslContext = SSLContext.getInstance("SSL");
    sslContext.init(null, trustAllCerts, new SecureRandom());
    HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());
  } catch (Exception e){
    e.printStackTrace();
  }
}

try {

  // Execute the method (Post) and set the results to the responseBodyAsString()
  int statusCode = client.executeMethod(method);
  resultsBody = method.getResponseBodyAsString();

} catch (HttpException e){
  e.printStackTrace();
} catch (IOException e){
  e.printStackTrace();
} finally {
  method.releaseConnection();
}

这是大家说要使用的忽略SSL证书错误的方法(仅在设置暂存环境时使用,不会在生产中使用)。但是,我仍然得到以下异常/堆栈跟踪:

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building unable to find valid certification path to requested target

有任何建议都很好,无论是我在使用TrustManager方面存在问题,还是我应该以不同的方式执行HTTP Post方法,都可以。

谢谢!


请阅读您发布的问题的答案(就像我一样,多次阅读)。我正在按照他所说的做,但仍然遇到问题。谢谢。 - andrewpthorp
请将您的代码粘贴到问题中,以便它是自包含的。对于像这样小的东西,无需使用Gist/Github。 - Bruno
“每个人”都对此有误解。忽略证书问题会使SSL极不安全,特别容易受到中间人攻击的威胁。请参阅RFC 2246中的讨论。还应该注意到,您链接中的X509TrustManager实现以及我看到的大多数其他实现都不符合规范。 - user207421
1
@EJP。发帖者并不是要求一堂安全课,他想知道如何做某件事情。 - Frak
4个回答

12

首先,不要忽略证书错误,而是应该处理它们。忽略证书错误会打开连接到潜在的中间人攻击。这就像关闭烟雾报警器中的蜂鸣器,因为有时会发出噪音...

当然,很容易说这只是测试代码,不会最终用于生产环境,但我们都知道在截止日期临近时会发生什么:当代码被测试时没有显示任何错误 -> 我们可以直接将其作为产品发布。如果需要,您应该设置一个测试 CA。它并不难制作,整个流程肯定比引入自定义代码进行开发并在生产中删除要容易得多。

你明显是在使用 Apache Http Client:

HttpClient client = new HttpClient();
int statusCode = client.executeMethod(method);

然而,您正在使用创建的SSLContext初始化javax.net.ssl.HttpsURLConnection

HttpsURLConnection.setDefaultSSLSocketFactory(sslContext.getSocketFactory());

这与Apache Http Client设置完全独立。

相反,您应该为Apache Http Client库设置SSLContext,如此答案中所述。如果您正在使用Apache Http Client 3.x,则需要设置自己的SecureProtocolSocketFactory以使用该SSLContext(请参见这里的示例)。不过,升级到直接支持SSLContext的Apache Http Client 4.x也是值得的。

您还可以使用Pascal的答案正确导入证书。同样,如果您按照该问题的已接受答案(由Kevin)进行操作,确实会忽略错误,但这将使连接容易受到MITM攻击。


谢谢!基本上,我已经编写了处理不同“环境”的代码,通过加载.properties文件来实现。我只在localhost.properties中将ignorecerterrors设置为true,这样它就不会悄悄地进入生产环境。这很好,我一定会研究一下的。我会告诉你的! - andrewpthorp
通过属性文件加载不同的环境是可行的,但您的代码库中仍然会有一些不安全的代码(可能会被其他人复制/粘贴)。总的来说,拥有一个内部测试 CA 可以进行更加真实的测试,而不需要进行代码更改(只需配置)。 - Bruno
我不太确定测试CA是什么,您能否给我解释一下? - andrewpthorp
好的,只需创建自己的CA并使用它颁发证书。在您的开发环境中,设置您的服务以使用这些证书,并将CA证书本身导入客户端的信任存储中。这在测试/开发环境中是可行的,并且有一些工具可以帮助您完成此操作(例如OpenSSL的CA.pl或TinyCA)。 - Bruno

2
如果你真的想忽略一切,这个方法适用于我:
SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new TrustStrategy() {
    @Override
    public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        return true;
    }
}).build();
HostnameVerifier hnv = new NoopHostnameVerifier();      
SSLConnectionSocketFactory sslcf = new SSLConnectionSocketFactory(sslContext, hnv);     
CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslcf).build()

2

HttpClient 4.3,非常简单易用,

     HttpClientBuilder cb = HttpClientBuilder.create();
     SSLContextBuilder sslcb = new SSLContextBuilder();
     sslcb.loadTrustMaterial(KeyStore.getInstance(KeyStore.getDefaultType()), new TrustSelfSignedStrategy());
     cb.setSslcontext(sslcb.build());
     CloseableHttpClient httpclient = cb.build();

那应该可以解决你的问题。

0

忽略证书会带来自己的危害,因为相同的代码可以在生产环境中运行,并可能造成混乱。

以下示例是使用Jersey Rest Client的:

private static final com.sun.jersey.api.client.Client client = com.sun.jersey.api.client.Client.create(configureClient());
final com.sun.jersey.api.client.WebResource webResource = client.resource("url");
try {
    com.sun.jersey.api.client.ClientResponse response = webResource.type(MediaType.APPLICATION_JSON_TYPE)
                            .accept(MediaType.APPLICATION_JSON_TYPE)
                            .get(com.sun.jersey.api.client.ClientResponse.class);
}catch(com.sun.jersey.api.client.ClientHandlerException che){
    che.printStackTrace();  
}

忽略证书可以如下:

public static ClientConfig configureClient() {
    TrustManager[ ] certs = new TrustManager[ ] {
            new X509TrustManager() {
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
                public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {}
            }
    };
    SSLContext ctx = null;
    try {
        ctx = SSLContext.getInstance("SSL");
        ctx.init(null, certs, new SecureRandom());
    } catch (java.security.GeneralSecurityException ex) {
    }
    HttpsURLConnection.setDefaultSSLSocketFactory(ctx.getSocketFactory());

    ClientConfig config = new DefaultClientConfig();
    try {
        config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, new HTTPSProperties(
                new HostnameVerifier() {
                    public boolean verify(String hostname, SSLSession session) {
                        return true;
                    }
                },
                ctx
        ));
    } catch(Exception e) {
    }

    return config;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接