Java 忽略 Apache HttpClient 4.3 中的 SSL 证书
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19517538/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Ignoring SSL certificate in Apache HttpClient 4.3
提问by Jakub M.
How to ignore SSL certificate (trust all) for Apache HttpClient 4.3?
如何忽略Apache HttpClient 4.3 的SSL 证书(全部信任)?
All the answers that I have found on SO treat previous versions, and the API changed.
我在 SO 上找到的所有答案都处理以前的版本,并且 API 发生了变化。
Related:
有关的:
- How to ignore SSL certificate errors in Apache HttpClient 4.0
- How to handle invalid SSL certificates with Apache HttpClient?
- Need to trust all the certificates during the development using Spring
- Ignore SSL Certificate Errors with Java
- 如何忽略 Apache HttpClient 4.0 中的 SSL 证书错误
- 如何使用 Apache HttpClient 处理无效的 SSL 证书?
- 使用 Spring 开发过程中需要信任所有证书
- 使用 Java 忽略 SSL 证书错误
Edit:
编辑:
- It is only for test purposes. Kids, don't try it at home (or in production)
- 它仅用于测试目的。孩子们,不要在家里(或在生产中)尝试
采纳答案by mavroprovato
The code below works for trusting self-signed certificates. You have to use the TrustSelfSignedStrategywhen creating your client:
下面的代码适用于信任自签名证书。创建客户端时,您必须使用TrustSelfSignedStrategy:
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
sslsf).build();
HttpGet httpGet = new HttpGet("https://some-server");
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} finally {
response.close();
}
I did not include the SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
on purpose: The point was to allow testing with self signed certificates so you don't have to acquire a proper certificate from a certification authority. You can easily create a self-signed certificate with the correct host name, so do that instead of adding the SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
flag.
我没有SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
故意包括:重点是允许使用自签名证书进行测试,因此您不必从证书颁发机构获取适当的证书。您可以使用正确的主机名轻松创建自签名证书,因此不要添加SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER
标志,而是这样做。
回答by eis
As an addition to the answer of @mavroprovato, if you want to trust all certificates instead of just self-signed, you'd do (in the style of your code)
作为@mavroprovato 答案的补充,如果您想信任所有证书而不仅仅是自签名,您会这样做(以您的代码风格)
builder.loadTrustMaterial(null, new TrustStrategy(){
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
});
or (direct copy-paste from my own code):
或(直接从我自己的代码中复制粘贴):
import javax.net.ssl.SSLContext;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.ssl.SSLContexts;
// ...
SSLContext sslContext = SSLContexts
.custom()
//FIXME to contain real trust store
.loadTrustMaterial(new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
})
.build();
And if you want to skip hostname verification as well, you need to set
如果你也想跳过主机名验证,你需要设置
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
sslsf).setSSLHostnameVerifier( NoopHostnameVerifier.INSTANCE).build();
as well. (ALLOW_ALL_HOSTNAME_VERIFIER is deprecated).
以及。(ALLOW_ALL_HOSTNAME_VERIFIER 已弃用)。
Obligatory warning: you shouldn't really do this, accepting all certificates is a bad thing. However there are some rare use cases where you want to do this.
强制性警告:你真的不应该这样做,接受所有证书是一件坏事。但是,在一些罕见的用例中,您想要执行此操作。
As a note to code previously given, you'll want to close response even if httpclient.execute() throws an exception
作为对之前给出的代码的说明,即使 httpclient.execute() 抛出异常,您也需要关闭响应
CloseableHttpResponse response = null;
try {
response = httpclient.execute(httpGet);
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
}
finally {
if (response != null) {
response.close();
}
}
Code above was tested using
上面的代码使用测试
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.3</version>
</dependency>
And for the interested, here's my full test set:
对于感兴趣的人,这是我的完整测试集:
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.TrustStrategy;
import org.apache.http.util.EntityUtils;
import org.junit.Test;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLHandshakeException;
import javax.net.ssl.SSLPeerUnverifiedException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class TrustAllCertificatesTest {
final String expiredCertSite = "https://expired.badssl.com/";
final String selfSignedCertSite = "https://self-signed.badssl.com/";
final String wrongHostCertSite = "https://wrong.host.badssl.com/";
static final TrustStrategy trustSelfSignedStrategy = new TrustSelfSignedStrategy();
static final TrustStrategy trustAllStrategy = new TrustStrategy(){
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
};
@Test
public void testSelfSignedOnSelfSignedUsingCode() throws Exception {
doGet(selfSignedCertSite, trustSelfSignedStrategy);
}
@Test(expected = SSLHandshakeException.class)
public void testExpiredOnSelfSignedUsingCode() throws Exception {
doGet(expiredCertSite, trustSelfSignedStrategy);
}
@Test(expected = SSLPeerUnverifiedException.class)
public void testWrongHostOnSelfSignedUsingCode() throws Exception {
doGet(wrongHostCertSite, trustSelfSignedStrategy);
}
@Test
public void testSelfSignedOnTrustAllUsingCode() throws Exception {
doGet(selfSignedCertSite, trustAllStrategy);
}
@Test
public void testExpiredOnTrustAllUsingCode() throws Exception {
doGet(expiredCertSite, trustAllStrategy);
}
@Test(expected = SSLPeerUnverifiedException.class)
public void testWrongHostOnTrustAllUsingCode() throws Exception {
doGet(wrongHostCertSite, trustAllStrategy);
}
@Test
public void testSelfSignedOnAllowAllUsingCode() throws Exception {
doGet(selfSignedCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE);
}
@Test
public void testExpiredOnAllowAllUsingCode() throws Exception {
doGet(expiredCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE);
}
@Test
public void testWrongHostOnAllowAllUsingCode() throws Exception {
doGet(expiredCertSite, trustAllStrategy, NoopHostnameVerifier.INSTANCE);
}
public void doGet(String url, TrustStrategy trustStrategy, HostnameVerifier hostnameVerifier) throws Exception {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(trustStrategy);
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
sslsf).setSSLHostnameVerifier(hostnameVerifier).build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} finally {
response.close();
}
}
public void doGet(String url, TrustStrategy trustStrategy) throws Exception {
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(trustStrategy);
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build());
CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(
sslsf).build();
HttpGet httpGet = new HttpGet(url);
CloseableHttpResponse response = httpclient.execute(httpGet);
try {
System.out.println(response.getStatusLine());
HttpEntity entity = response.getEntity();
EntityUtils.consume(entity);
} finally {
response.close();
}
}
}
(working test project in github)
(github 中的工作测试项目)
回答by vasekt
If you are using PoolingHttpClientConnectionManager procedure above doesn't work, custom SSLContext is ignored. You have to pass socketFactoryRegistry in contructor when creating PoolingHttpClientConnectionManager.
如果您使用的 PoolingHttpClientConnectionManager 过程不起作用,则自定义 SSLContext 将被忽略。创建 PoolingHttpClientConnectionManager 时,您必须在构造函数中传递 socketFactoryRegistry。
SSLContextBuilder builder = SSLContexts.custom();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
return true;
}
});
SSLContext sslContext = builder.build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sslContext, new X509HostnameVerifier() {
@Override
public void verify(String host, SSLSocket ssl)
throws IOException {
}
@Override
public void verify(String host, X509Certificate cert)
throws SSLException {
}
@Override
public void verify(String host, String[] cns,
String[] subjectAlts) throws SSLException {
}
@Override
public boolean verify(String s, SSLSession sslSession) {
return true;
}
});
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory> create().register("https", sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(
socketFactoryRegistry);
CloseableHttpClient httpclient = HttpClients.custom()
.setConnectionManager(cm).build();
回答by migo
One small addition to the answer by vasekt:
vasekt 回答的一个小补充:
The provided solution with the SocketFactoryRegistry works when using PoolingHttpClientConnectionManager.
使用 PoolingHttpClientConnectionManager 时,提供的 SocketFactoryRegistry 解决方案有效。
However, connections via plain http don't work any longer then. You have to add a PlainConnectionSocketFactory for the http protocol additionally to make them work again:
但是,那时通过普通 http 的连接不再起作用。您必须另外为 http 协议添加一个 PlainConnectionSocketFactory 以使它们再次工作:
Registry<ConnectionSocketFactory> socketFactoryRegistry =
RegistryBuilder.<ConnectionSocketFactory> create()
.register("https", sslsf)
.register("http", new PlainConnectionSocketFactory()).build();
回答by rich95
(I would have added a comment directly to vasekt's answer but I don't have enough reputation points (not sure the logic there)
(我会直接在 vasekt 的回答中添加评论,但我没有足够的声望点(不确定那里的逻辑)
Anyway... what I wanted to say is that even if you aren't explicitly creating/asking for a PoolingConnection, doesn't mean you aren't getting one.
无论如何...我想说的是,即使您没有明确创建/请求 PoolingConnection,也不意味着您没有得到一个。
I was going crazy trying to figure out why the original solution didn't work for me, but I ignored vasekt's answer as it "didn't apply to my case" - wrong!
我疯狂地想弄清楚为什么原来的解决方案对我不起作用,但我忽略了 vasekt 的回答,因为它“不适用于我的情况” - 错了!
I was staring at my stack-trace when low and behold I saw a PoolingConnection in the middle of it. Bang - I tired his addition and success!! (our demo is tomorrow and I was getting desperate) :-)
低时我正盯着我的堆栈跟踪,看哪,我在它的中间看到了一个 PoolingConnection。Bang - 我厌倦了他的加入和成功!!(我们的演示是明天,我快绝望了):-)
回答by Saurabh
After trying various options, following configuration worked for both http and https
尝试各种选项后,以下配置适用于 http 和 https
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build(),SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", new PlainConnectionSocketFactory())
.register("https", sslsf)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
cm.setMaxTotal(2000);//max connection
//System.setProperty("jsse.enableSNIExtension", "false"); //""
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLSocketFactory(sslsf)
.setConnectionManager(cm)
.build();
I am using http-client 4.3.3 -
我正在使用 http-client 4.3.3 -
compile 'org.apache.httpcomponents:httpclient:4.3.3'
compile 'org.apache.httpcomponents:httpclient:4.3.3'
回答by Dahai Li
class ApacheHttpClient {
/***
* This is a https get request that bypasses certificate checking and hostname verifier.
* It uses basis authentication method.
* It is tested with Apache httpclient-4.4.
* It dumps the contents of a https page on the console output.
* It is very similar to http get request, but with the additional customization of
* - credential provider, and
* - SSLConnectionSocketFactory to bypass certification checking and hostname verifier.
* @param path String
* @param username String
* @param password String
* @throws IOException
*/
public void get(String path, String username, String password) throws IOException {
final CloseableHttpClient httpClient = HttpClients.custom()
.setDefaultCredentialsProvider(createCredsProvider(username, password))
.setSSLSocketFactory(createGenerousSSLSocketFactory())
.build();
final CloseableHttpResponse response = httpClient.execute(new HttpGet(path));
try {
HttpEntity entity = response.getEntity();
if (entity == null)
return;
System.out.println(EntityUtils.toString(entity));
} finally {
response.close();
httpClient.close();
}
}
private CredentialsProvider createCredsProvider(String username, String password) {
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
AuthScope.ANY,
new UsernamePasswordCredentials(username, password));
return credsProvider;
}
/***
*
* @return SSLConnectionSocketFactory that bypass certificate check and bypass HostnameVerifier
*/
private SSLConnectionSocketFactory createGenerousSSLSocketFactory() {
SSLContext sslContext;
try {
sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, new TrustManager[]{createGenerousTrustManager()}, new SecureRandom());
} catch (KeyManagementException | NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
return new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE);
}
private X509TrustManager createGenerousTrustManager() {
return new X509TrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] cert, String s) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] cert, String s) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
}
}
回答by Hamed MP
Simpler and shorter working code:
更简单和更短的工作代码:
We are using HTTPClient 4.3.5 and we tried almost all solutions exist on the stackoverflow but nothing, After thinking and figuring out the problem, we come to the following code which works perfectly, just add it before creating HttpClient instance.
some method which you use to make post request...
我们使用的是HTTPClient 4.3.5,我们尝试了stackoverflow上几乎所有存在的解决方案,但没有任何解决方案,经过思考和弄清楚问题,我们得出以下完美运行的代码,只需在创建HttpClient实例之前添加它。
您用来提出发布请求的某些方法...
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
return true;
}
});
SSLConnectionSocketFactory sslSF = new SSLConnectionSocketFactory(builder.build(),
SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslSF).build();
HttpPost postRequest = new HttpPost(url);
continue calling and using HttpPost instance in the normal form
继续以正常形式调用和使用 HttpPost 实例
回答by Kurt
When using http client 4.5 I had to use the javasx.net.ssl.HostnameVerifier to allow any hostname (for testing purposes). Here is what I ended up doing:
使用 http 客户端 4.5 时,我必须使用 javasx.net.ssl.HostnameVerifier 来允许任何主机名(用于测试目的)。这是我最终做的:
CloseableHttpClient httpClient = null;
try {
SSLContextBuilder sslContextBuilder = new SSLContextBuilder();
sslContextBuilder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
HostnameVerifier hostnameVerifierAllowAll = new HostnameVerifier()
{
public boolean verify(String hostname, SSLSession session) {
return true;
}
};
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContextBuilder.build(), hostnameVerifierAllowAll);
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials(
new AuthScope("192.168.30.34", 8443),
new UsernamePasswordCredentials("root", "password"));
httpClient = HttpClients.custom()
.setSSLSocketFactory(sslSocketFactory)
.setDefaultCredentialsProvider(credsProvider)
.build();
HttpGet httpGet = new HttpGet("https://192.168.30.34:8443/axis/services/getStuff?firstResult=0&maxResults=1000");
CloseableHttpResponse response = httpClient.execute(httpGet);
int httpStatus = response.getStatusLine().getStatusCode();
if (httpStatus >= 200 && httpStatus < 300) { [...]
} else {
throw new ClientProtocolException("Unexpected response status: " + httpStatus);
}
} catch (Exception ex) {
ex.printStackTrace();
}
finally {
try {
httpClient.close();
} catch (IOException ex) {
logger.error("Error while closing the HTTP client: ", ex);
}
}
回答by Jeff Z
Trust All Certs in Apache HTTP Client
信任 Apache HTTP 客户端中的所有证书
TrustManager[] trustAllCerts = new TrustManager[]{
new X509TrustManager() {
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
public void checkClientTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
public void checkServerTrusted(
java.security.cert.X509Certificate[] certs, String authType) {
}
}
};
try {
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
sc);
httpclient = HttpClients.custom().setSSLSocketFactory(
sslsf).build();
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());