记一次https访问握手失败(handshake failure)
文章作者:luxianghao
文章来源:http://www.cnblogs.com/luxianghao/p/6239518.html 转载请注明,谢谢合作。
免责声明:文章内容仅代表个人观点,如有不当,欢迎指正。
---
事情起因
某日,突然之前ok的访问不ok了,具体情况如下
[root@host tmp]# curl https://cdn.example.com/monitor/test.txt
This is a test object.

那么基本可以排除不是证书的问题了,而且把curl的verbose/debug模式打开也看到,ssl认证是ok的,如下
* Connected to cdn.example.com (52.222.238.79) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* CAfile: /etc/pki/tls/certs/ca-bundle.crt
CApath: none
* SSL connection using TLS_RSA_WITH_AES_256_CBC_SHA
* Server certificate:
* subject: CN=cdn.example.com,OU=Domain Control Validated
* start date: May 23 06:57:38 2016 GMT
* expire date: May 23 06:57:38 2019 GMT
* common name: cdn.example.com
* issuer: CN=Go Daddy Secure Certificate Authority - G2,OU=http://certs.godaddy.com/repository/,O="GoDaddy.com, Inc.",L=Scottsdale,ST=Arizona,C=US
> GET /monitor/test.txt HTTP/1.1
> User-Agent: curl/7.19.7 (x86_64-redhat-linux-gnu) libcurl/7.19.7 NSS/3.13.1.0 zlib/1.2.3 libidn/1.18 libssh2/1.2.2
> Host: cdn.example.com
> Accept: */*
还没见过这种情况,google之,
看到如下链接中有类似的情况:老版本的wget(before1.12)因不支持SNI,导致不能验证证书
https://bugzilla.redhat.com/show_bug.cgi?id=909604
那么开始用高版本的wget(1.15)测试,成功访问
root@host:~$ wget https://cdn.example.com/monitor/test.txt
--2016-12-31 15:54:36-- https://cdn.example.com/monitor/test.txt
Resolving cdn.example.com (cdn.example.com)... 54.230.111.111, 54.230.111.48, 54.230.111.191, ...
Connecting to cdn.example.com (cdn.example.com)|54.230.111.111|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 22 [text/plain]
Saving to: ‘test.txt’
100%[===================================================================================================================================================================>] 22 --.-K/s in 0s
2016-12-31 15:54:42 (5.58 MB/s) - ‘test.txt’ saved [22/22]
root@host~$ cat test.txt
This is a test object.
什么是SNI
有的同学可能还不太清楚SNI,refer to https://en.wikipedia.org/wiki/Server_Name_Indication
SNI是Server Name Indication的简称,即服务器名称指示,其作用是
允许在相同的IP地址和TCP端口号的服务器上使用多个证书,而不必所有网站都使用同一个证书。
上面的维基百科中也明确的给了什么软件的什么版本支持SNI,由于我们服务的用户用的Java客户端,
对于java来说1.7及其以后的版本是支持SNI的,so, 我们继续验证测试
首先用Java1.6测试:
[root@host java]# /opt/soft/jdk1.6.0_37/bin/javac TestGetPost.java
[root@host java]# /opt/soft/jdk1.6.0_37/bin/java TestGetPost
发送GET请求出现异常!javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:174)
at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Alerts.java:136)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1839)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1019)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1203)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1230)
at com.sun.net.ssl.internal.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1214)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:434)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:166)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:133)
at TestGetPost.sendGet(TestGetPost.java:35)
at TestGetPost.main(TestGetPost.java:129)
https://cdn.example.com/monitor/test.txt
然后用Java1.7测试
[root@host java]# /opt/soft/jdk1.7/bin/javac TestGetPost.java
[root@host java]# /opt/soft/jdk1.7/bin/java TestGetPost
Picked up _JAVA_OPTIONS: -Xmx2048m -XX:MaxPermSize=512m -Djava.awt.headless=true
null--->[HTTP/1.1 200 OK]
Access-Control-Expose-Headers--->[content-md5, upload-time, x-xiaomi-meta-content-length]
Content-Length--->[22]
Last-Modified--->[Wed, 01 Jun 2016 08:53:11 GMT]
Access-Control-Max-Age--->[1728000]
X-Amz-Cf-Id--->[Mey-pVjsfKekWVmKX_7U0iZ_7MollPIAzN0HY5V9YnfBe5LtXDHDUA==]
Access-Control-Allow-Methods--->[GET, POST, PUT, HEAD, DELETE, OPTIONS]
Connection--->[keep-alive]
Access-Control-Allow-Credentials--->[true]
X-Cache--->[Miss from cloudfront]
Server--->[Tengine]
Cache-Control--->[no-cache]
Access-Control-Allow-Headers--->[DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization,Content-MD5]
Date--->[Sat, 31 Dec 2016 08:13:31 GMT]
Content-MD5--->[fac2cbcd7b7417c0325922b689019c65]
Via--->[1.1 04ad4dd44cc71948e73ac52ffdeebc8a.cloudfront.net (CloudFront)]
Content-Type--->[text/plain]
https://cdn.example.com/monitor/test.txt
/nThis is a test object.
用Java1.8测试结果同1.7
Java测试代码
[root@host java]# cat TestGetPost.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 | import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.net.URLConnection; import java.util.List; import java.util.Map; public class TestGetPost { /** * 向指定URL发送GET方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是name1=value1&name2=value2的形式。 * @return URL所代表远程资源的响应 */ public static String sendGet(String url, String param) { String result = "" ; BufferedReader in = null ; try { String urlName = url + "?" + param; URL realUrl = new URL(urlName); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty( "accept" , "*/*" ); conn.setRequestProperty( "connection" , "Keep-Alive" ); conn.setRequestProperty( "user-agent" , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" ); // 建立实际的连接 conn.connect(); // 获取所有响应头字段 Map<String, List<String>> map = conn.getHeaderFields(); // 遍历所有的响应头字段 for (String key : map.keySet()) { System.out.println(key + "--->" + map.get(key)); } // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null ) { result += "/n" + line; } } catch (Exception e) { System.out.println( "发送GET请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输入流 finally { try { if (in != null ) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } /** * 向指定URL发送POST方法的请求 * * @param url * 发送请求的URL * @param param * 请求参数,请求参数应该是name1=value1&name2=value2的形式。 * @return URL所代表远程资源的响应 */ public static String sendPost(String url, String param) { PrintWriter out = null ; BufferedReader in = null ; String result = "" ; try { URL realUrl = new URL(url); // 打开和URL之间的连接 URLConnection conn = realUrl.openConnection(); // 设置通用的请求属性 conn.setRequestProperty( "accept" , "*/*" ); conn.setRequestProperty( "connection" , "Keep-Alive" ); conn.setRequestProperty( "user-agent" , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)" ); // 发送POST请求必须设置如下两行 conn.setDoOutput( true ); conn.setDoInput( true ); // 获取URLConnection对象对应的输出流 out = new PrintWriter(conn.getOutputStream()); // 发送请求参数 out.print(param); // flush输出流的缓冲 out.flush(); // 定义BufferedReader输入流来读取URL的响应 in = new BufferedReader( new InputStreamReader(conn.getInputStream())); String line; while ((line = in.readLine()) != null ) { result += "/n" + line; } } catch (Exception e) { System.out.println( "发送POST请求出现异常!" + e); e.printStackTrace(); } // 使用finally块来关闭输出流、输入流 finally { try { if (out != null ) { out.close(); } if (in != null ) { in.close(); } } catch (IOException ex) { ex.printStackTrace(); } } return result; } // 提供主方法,测试发送GET请求和POST请求 public static void main(String args[]) { //发送GET请求 String str = new String( "https://cdn.example.com/monitor/test.txt" ); String s = TestGetPost.sendGet(str, null ); System.out.println(str); System.out.println(s); } } |
结论
至此,基本可以确认是客户端不支持SNI是造成https访问失败的原因了,但也不排除是其他原因造成,待续。。。。。。
posted on 2016-12-31 16:36 luxianghao 阅读(33121) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人