Java 8+HttpClient 4.3.5通过代理无法连接HTTPS的问题
问题:Apache HttpClient 4.3.5通过代理访问HTTPS失败,HTTP正常
我使用Apache HttpClient 4.3.5编写Java代码通过代理访问网络,HTTP地址可正常返回IP,但HTTPS地址抛出javax.net.ssl.SSLException: Received fatal alert: internal_error异常。已配置忽略所有证书验证,该问题在Java 8 build 181和Java 19环境中均会出现。
代码实现
import org.apache.http.HttpHost; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpRequestBase; import org.apache.http.client.protocol.HttpClientContext; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.conn.socket.PlainConnectionSocketFactory; import org.apache.http.conn.ssl.NoopHostnameVerifier; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.BasicCredentialsProvider; import org.apache.http.impl.client.BasicResponseHandler; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContextBuilder; import org.junit.Test; import java.net.Authenticator; import java.net.URI; import java.security.KeyManagementException; import java.security.NoSuchAlgorithmException; public class ConnectToHttpsSafelyTest { @Test public void httpAndHttpsWithProxyTest() throws Exception { System.out.println(executeHttpCall("http://myexternalip.com/raw")); System.out.println(executeHttpCall("https://myexternalip.com/raw")); } private Object executeHttpCall(final String url) throws Exception{ final SSLContextBuilder sslBuilder = new SSLContextBuilder(); sslBuilder.loadTrustMaterial(null,(chain,authType)->true); final HttpClientBuilder httpClient = HttpClientBuilder.create(); setUpProxy(httpClient,sslBuilder); final HttpRequestBase request = new HttpGet(); ((HttpGet) request).setURI(URI.create(url)); final Object obj = httpClient.build().execute(request, new BasicResponseHandler(), HttpClientContext.create()); return obj; } private void setUpProxy(HttpClientBuilder builder, SSLContextBuilder sslBuilder) throws NoSuchAlgorithmException, KeyManagementException { final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslBuilder.build(), new String[]{"SSLv2Hello", "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE); Authenticator.setDefault(null); HttpHost httpProxy = new HttpHost("proxyHost", proxyPort, "http"); CredentialsProvider cred_provider = new BasicCredentialsProvider(); cred_provider.setCredentials(new AuthScope(httpProxy), new UsernamePasswordCredentials("proxyUser", "proxyPassword")); final PoolingHttpClientConnectionManager conn_mgr = new PoolingHttpClientConnectionManager(RegistryBuilder. <ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslConnectionSocketFactory) .build()); conn_mgr.setDefaultMaxPerRoute(Integer.MAX_VALUE); conn_mgr.setMaxTotal(Integer.MAX_VALUE); builder.setConnectionManager(conn_mgr) .setProxy(httpProxy) .setDefaultCredentialsProvider(cred_provider); builder.setSSLSocketFactory(sslConnectionSocketFactory); } }
异常信息
220.1.72.193 javax.net.ssl.SSLException: Received fatal alert: internal_error at sun.security.ssl.Alerts.getSSLException(Alerts.java:208) at sun.security.ssl.Alerts.getSSLException(Alerts.java:154) at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:2038) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1135) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:396) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.upgrade(DefaultHttpClientConnectionOperator.java:193) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.upgrade(PoolingHttpClientConnectionManager.java:389) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:416) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:237) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:185) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:111) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:72) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:221) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:165)
解决方案
1. 移除重复的SSL连接工厂配置
代码中同时给连接管理器注册了SSL连接工厂,又调用builder.setSSLSocketFactory(),这会导致HttpClient内部配置冲突。直接删除这行代码:
builder.setSSLSocketFactory(sslConnectionSocketFactory);
2. 调整TLS协议版本
禁用过时的SSLv2Hello、SSLv3协议,这些协议可能被代理或目标服务器拒绝,只保留现代TLS版本:
final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslBuilder.build(), new String[]{"TLSv1.2", "TLSv1.3"}, // 优先使用TLSv1.3,不支持则降级到TLSv1.2 null, NoopHostnameVerifier.INSTANCE);
3. 修正连接池配置
不要将连接池最大连接数设置为Integer.MAX_VALUE,这会导致系统资源耗尽,改为合理的值:
conn_mgr.setDefaultMaxPerRoute(20); // 每个路由的最大连接数 conn_mgr.setMaxTotal(100); // 连接池总最大连接数
4. 确认代理支持CONNECT隧道
HTTPS通过代理访问需要代理服务器支持CONNECT方法,请联系运维确认代理是否允许CONNECT请求,以及是否开放了443端口的隧道权限。
5. 启用SSL调试排查
添加JVM启动参数启用SSL握手调试,查看详细错误信息:
-Djavax.net.debug=ssl:handshake:verbose
根据日志可以定位是协议不匹配、代理认证失败还是目标服务器证书问题。
优化后的核心代码
private void setUpProxy(HttpClientBuilder builder, SSLContextBuilder sslBuilder) throws NoSuchAlgorithmException, KeyManagementException { final SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(sslBuilder.build(), new String[]{"TLSv1.2", "TLSv1.3"}, null, NoopHostnameVerifier.INSTANCE); Authenticator.setDefault(null); HttpHost httpProxy = new HttpHost("proxyHost", proxyPort, "http"); CredentialsProvider cred_provider = new BasicCredentialsProvider(); cred_provider.setCredentials(new AuthScope(httpProxy), new UsernamePasswordCredentials("proxyUser", "proxyPassword")); final PoolingHttpClientConnectionManager conn_mgr = new PoolingHttpClientConnectionManager(RegistryBuilder. <ConnectionSocketFactory>create() .register("http", PlainConnectionSocketFactory.getSocketFactory()) .register("https", sslConnectionSocketFactory) .build()); conn_mgr.setDefaultMaxPerRoute(20); conn_mgr.setMaxTotal(100); builder.setConnectionManager(conn_mgr) .setProxy(httpProxy) .setDefaultCredentialsProvider(cred_provider); // 已移除builder.setSSLSocketFactory调用 }
内容的提问来源于stack exchange,提问作者chiperortiz
相关产品推荐
相关产品推荐

