记一次https访问握手失败(handshake failure)

   文章作者:luxianghao

   文章来源:http://www.cnblogs.com/luxianghao/p/6239518.html  转载请注明,谢谢合作。

   免责声明:文章内容仅代表个人观点,如有不当,欢迎指正。

   ---

事情起因

某日,突然之前ok的访问不ok了,具体情况如下

[root@host tmp]# wget https://cdn.example.com/monitor/test.txt   
--2016-12-22 12:57:34--  https://cdn.example.com/monitor/test.txt
Resolving cdn.example.com... 52.222.238.45, 52.222.238.96, 52.222.238.218, ...
Connecting to example.com|52.222.238.45|:443... connected.
OpenSSL: error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
Unable to establish SSL connection.
 
尝试用curl,浏览器访问
很意外,竟然访问成功了

[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   luxianghao  阅读(33121)  评论(0编辑  收藏  举报

编辑推荐:
· 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训练数据并当服务器共享给他人

导航

< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5
点击右上角即可分享
微信分享提示