1.问题报告
前两天在客户现场环境的同学,在进行跟第三方业务联调的过程中出现了一个https调用的错误,具体报错信息为javax.net.ssl.SSLException:Unrecognized SSL message,plaintext connection?
2.问题分析
这个错误意思是说,无法识别 SSL 信息,明文连接?看这个意思是说在使用 https 协议访问网络资源时无法识别 SSL 信息。但是在我们将程序发布客户现场前已经充分测试通过的,为什么在客户现场就会出现这个错误呢?
SSL(Secure Socket Layer 安全套接层)是基于HTTPS下的一个协议加密层,最初是由网景公司(Netscape)研发,后被IETF(The Internet Engineering Task Force - 互联网工程任务组)标准化后写入(RFCRequest For Comments 请求注释),RFC里包含了很多互联网技术的规范! 起初是因为HTTP在传输数据时使用的是明文(虽然说POST提交的数据时放在报体里看不到的,但是还是可以通过抓包工具窃取到)是不安全的,为了解决这一隐患网景公司推出了SSL安全套接字协议层,SSL是基于HTTP之下TCP之上的一个协议层,是基于HTTP标准并对TCP传输数据时进行加密,所以HPPTS是HTTP+SSL/TCP的简称。 SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持。SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。 SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
3.初步方案
本来 https 是在 http 的基础上进行加密。使用 SSL 协议进行加密。这样通讯的双方在通讯前就要去做身份校验,通过证书的方式验证身份。原来是证书方面的问题,需要我们调整一下代码,使其信任所有证书(忽略证书的校验),根据不同的HTTP请求工具会有所不同,我们这里用的是HttpClient
,不同的版本的写法有所不同,不过整体思路是一致的,就是信任所有证书
Ø HttpClient <4.3版本
现在让我们将HTTPClient配置为信任所有证书,无论其有效性如何
@Test public final void test() throws GeneralSecurityException { String urlOverHttps = "https://www.baidu.com/"; HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); CloseableHttpClient httpClient = (CloseableHttpClient) requestFactory.getHttpClient(); //信用所有证书 TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; //NoopHostnameVerifier类: 作为主机名验证工具,实质上关闭了主机名验证,它接受任何 //有效的SSL会话并匹配到目标主机 SSLSocketFactory sf = new SSLSocketFactory(acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER); httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", 8443, sf)); ResponseEntity<String> response = new RestTemplate(requestFactory). exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }
Ø HttpClient 4.4及更高版本
使用新的HTTPClient,现在我们有了一个增强的,重新设计的默认SSL主机名验证程序。此外,通过引入SSLConnectionSocketFactory和RegistryBuilder,可以轻松构建
SSLSocketFactory
@Test public final void test() throws GeneralSecurityException { String urlOverHttps = "https://www.baidu.com/"; //信用所有证书 TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("https", sslsf) .register("http", new PlainConnectionSocketFactory()) .build(); BasicHttpClientConnectionManager connectionManager = new BasicHttpClientConnectionManager(socketFactoryRegistry); CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf) .setConnectionManager(connectionManager).build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); ResponseEntity<String> response = new RestTemplate(requestFactory) .exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }
4.初次回归
根据对应的解决方案,我们同步调整对程序使用HttpClient封装的HTTP请求工具类进行类调整,然后重新发布客户现回归测试发现仍然存在同样的SSL错误,怎么会这样呢?
/** * @description 连接池配置 * 采用PoolingHttpClientConnectionManager 可以为多线程提供并发HTTP请求服务。主要作用就是分配连接、 * 回收连接等..当请求一个新的连接时,如果连接池有有可用的持久连接,连接管理器就会使用其中的一个, * 而不是再创建一个新的连接 * @author songyl * @date 2019年8月15日 上午12:51:20 */ private static void configPoolMgr() { try { /**Http连接使用java.net.Socket类来传输数据。这依赖于ConnectionSocketFactory接口来创建、 初始化和连接socket。这样也就允许HttpClient的用户在代码运行时,指定socket初始化的代码**/ ConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory(); //配置通用的SSL TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build(); sslsf = new SSLConnectionSocketFactory(sslContext, NoopHostnameVerifier.INSTANCE); //自定义的socket工厂类可以和指定的协议(Http、Https)联系起来,用来创建自定义的连接管理器 Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create() .register("http", plainsf) .register("https", sslsf) .build(); //连接池管理器 connMgr = new PoolingHttpClientConnectionManager(registry); //最大连接数 connMgr.setMaxTotal(MAX_TOTAL); //每个路由基础的连接数 connMgr.setDefaultMaxPerRoute(ROUTE_MAX_TOTAL); } catch (Exception e) { LOGGER.error("configPoolMgr error: ", e); // throw new TbspBaseRuntimeException("连接池配置异常", e); } }
/** * @return * @description 获取HttpClient * 添加 PoolingHttpClientConnectionManager connMgr 连接池 * 添加 HttpRequestRetryHandler retryHandler 重试机制 * @author songyl * @date 2019年8月15日 上午1:08:25 */ private static CloseableHttpClient getHttpClient() { HttpClientBuilder httpClientBuilder = HttpClients.custom(); httpClientBuilder.setConnectionManager(connMgr); httpClientBuilder.setRetryHandler(retryHandler); httpClientBuilder.setSSLSocketFactory(sslsf); //创建httpClient return httpClientBuilder.build(); }
5.最终方案
通过忽略SSL证书在客户现场并没有把问题给解决掉,这个时候我们只能再分析客户现场跟我们本地有什么差异,通过了解客户现场再网络链路上对外请求HTTP增加了一层代理服务器,差异如下,客户现场多了代理服务器这一段网络链路。
因此在通过代理服务器访问第三方目标地址时,需要在setProxy()方法中设置代理IP后可以将url中的域名换成这个代理IP,http很简单,但是https这样会报错。
问题:如何使用代理发送https请求?
客户端发送https请求之前会先向这台服务器请求ssl证书,并在客户端对这个证书做一个校验。
而使用代理IP时,实际上请求打到了这个代理IP上,而客户端并不知道这件事,他仍然在等待url域名中所对应的ssl证书,而这代理IP对应的服务器实际上并没有这个证书,导致了https请求失败。
解决办法:在忽略SSL证书验证的同时应该显式的配置代理地址,通过代理转发到目标地址
// 代理信息 protected static String proxyUrl; /** * @return * @description 获取HttpClient * 添加 PoolingHttpClientConnectionManager connMgr 连接池 * 添加 HttpRequestRetryHandler retryHandler 重试机制 * @author songyl * @date 2019年8月15日 上午1:08:25 */ private static CloseableHttpClient getHttpClient() { HttpClientBuilder httpClientBuilder = HttpClients.custom(); httpClientBuilder.setConnectionManager(connMgr); httpClientBuilder.setRetryHandler(retryHandler); httpClientBuilder.setSSLSocketFactory(sslsf); if (proxyUrl != null) { HttpHost proxy = HttpHost.create(proxyUrl); DefaultProxyRoutePlanner routePlanner = new DefaultProxyRoutePlanner(proxy); httpClientBuilder.setRoutePlanner(routePlanner); } //创建httpClient return httpClientBuilder.build(); }
6.再次回归
再次发布客户现场回归测试后我们发现SSL异常问题已彻底解决,同时通过请求日志我们会发现请求底层会进行一次代理服务器的路由转发
7.问题总结
通过这个问题的分析跟解决我发现在我们平时做一些基础工具的研发过程中,由于我们所服务的客户的特点(客户存在本地化部署&云部署模式)我们需要更多的考虑客户现场跟我们本地的一些网络链路上的差异,尽可能的考虑程序的兼容性及健壮性,在这个问题上我们初期就应该考虑直连与代理访问两种情况。
相关推荐
openjdk 遇到的 HTTPS 的 SSL 异常解决方案 在使用 openjdk 时,可能会遇到 HTTPS 的 SSL 异常问题,例如在使用 HTTPS 连接时出现 KeyException 异常。本文将提供两套解决方案来解决这个问题。 一、卸载 openjdk,...
在使用pip3的时候,错误信息如下...而且网上的某些方法用./configure –with-ssl这条命令是无效的,下面给出解决问题的方案。 出现这种错误可以在Modules/Setup中,Ctrl + F查找字符串SSL, 将找到的第一个SSL下面的命
### Win7配置Tomcat6与SSL问题及解决方案 在配置Windows 7系统下的Apache Tomcat 6服务器并启用SSL加密时,遇到了一些常见问题。本文将详细介绍这些问题的具体表现、原因分析以及相应的解决策略。 #### 一、Win7 ...
Foxmail 中 SSL 连接错误解决方案 在 Foxmail 中,SSL 连接错误是一个常见的问题,许多用户都曾经遇到过这种错误。那么,如何解决这个问题呢?下面我们将详细介绍解决 Foxmail 中 SSL 连接错误的步骤。 首先,让...
以下是一些常见的错误及其解决方案: 错误一:SMTP 服务器要求安全连接或客户端未通过身份验证(5.7.1 Client was not authenticated) 这个错误通常意味着SMTP服务器需要一个安全的连接(例如,通过SSL或TLS加密)...
2. 使用 `RemoteCertificateValidationCallback`:这个解决方案是指客户端使用一个回调函数来验证服务器的证书。这个解决方案可以提供更好的安全性。 在上面的代码中,我们使用了 `ServicePointManager` 来配置 SSL...
- **禁用证书验证**:这是一个临时的解决方案,但不推荐,因为它削弱了HTTPS的安全性。在代码中可以通过设置`SSLSocketFactory`或`TrustManager`来实现。 - **配置自定义TrustManager**:如果你知道将要连接的...
一、引言 当我们的Linux服务器上当中发布了web项目,有时候需要配置一个SSL证书,这样表示你这...二、错误解决步骤 既然在安装的时候没有编译ssl,难道把nginx卸载重新安装一次?不不不,我们只需要在原有的基础上添加
因此,本文将对Foxmail常见的错误提示进行总结和解析,并提供相应的解决方案。 Foxmail常见错误提示 1. 连接失败:由于连接方在一段时间后没有正确答复或连接的主机没有反应,连接尝试失败。 解决方案:使用收发...
面对“Field 'ssl_cipher' doesn't have a default value”的错误,最佳解决方案是使用`GRANT`语句来替代传统的`INSERT`操作。这样不仅可以避免因字段缺失默认值而引发的错误,还能更简洁地完成用户创建和授权的过程...
"MongoDB的强事务解决方案" MongoDB是一个流行的NoSQL数据库管理系统,它提供了强的事务解决方案,以满足现代化应用程序的需求。本文档将详细介绍MongoDB的强事务解决方案,包括ACID事务、事务补偿设计模式、...
这篇文章主要介绍了Python SSL证书验证问题解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一、SSL问题 1、在你不启用fiddler时,python代码直接发送...
本文将深入探讨这个问题及其解决方案。 首先,我们要明白SSL动态库在Delphi7和Indy9中的作用。libeay32.dll和ssleay32.dll是OpenSSL库的两个关键组件,它们包含了实现SSL/TLS协议所需的加密算法和函数。当Indy9在...
本文将详细讨论在与SQL Server建立SSL加密连接时遇到的问题以及解决方案。 标题提及的“无法通过SSL加密无法与SQL Server产生连接”通常涉及到几个关键问题:服务器配置、客户端支持、证书问题和网络设置。首先,...
尽管如此,我们可以从标题“金融行业网络安全解决方案.pdf”出发,结合给定的重复内容片段,对金融行业的网络安全解决方案进行深入解析。 网络安全在金融行业的重要性不言而喻,金融行业由于其业务的特殊性,对信息...
### 常见问题及解决方案 #### 1.6.1 错误1:配置Apache时SSL/TLS toolkit错误 如果在配置Apache时遇到`invalid SSL/TLS toolkit basedirectory /usr/local/ssl`错误,那是因为系统中未安装OpenSSL。解决方法是先...
解决这个问题的关键在于安装并信任相应的SSL根证书。Charles在进行HTTPS请求的中间人攻击(MITM)时,需要将自身的根证书安装到被测试的系统中,这样Charles就能够将加密的请求解密,然后再进行抓包分析。如果用户...
6. **错误处理机制**:在代码中添加适当的错误处理机制,当SSL连接失败时,能够有备选方案,例如降级到非HTTPS连接(虽然安全性降低,但有时可作为临时解决方案)。 7. **联系腾讯云支持**:如果以上方法无法解决...
在这个解决方案中,用户可能可以找到如何配置和使用这两个模块的详细步骤,包括如何编写AT命令来建立和断开GPRS连接,发送和接收数据,以及如何处理错误和异常。同时,可能还涵盖了网络连接的优化策略,例如如何有效...
#### 三、解决方案 下面详细介绍两种常见的解决方法: ##### 1. 使用Java系统属性 这种方法通过在启动Kettle时设置特定的Java系统属性来信任所有SSL证书。这种方式简单快捷,适用于快速测试和开发环境。 - **...