文章作者: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

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访问失败的原因了,但也不排除是其他原因造成,待续。。。。。。

最新文章

  1. iOS 支付宝的使用
  2. oracle 11g express 快速入门
  3. Caused by: java.lang.UnsatisfiedLinkError...解决经历
  4. 全面理解JavaScript中的闭包的含义及用法
  5. js与java通信
  6. ios Objective-C的动态特性
  7. 关于 iOS socket 都在这里了
  8. codeforces 613A. Peter and Snow Blower
  9. node.js 发送http 请求
  10. 云游戏学习与实践(二)——安装GamingAnywhere
  11. python——绘制二元高斯分布的三维图像,
  12. 开放源代码的设计层面框架Spring——day03
  13. zabbix 安装rpm
  14. JVM基础系列第8讲:JVM 垃圾回收机制
  15. A - Exposition CodeForces - 6E
  16. C# 设计模式速记
  17. 论文笔记之:DualGAN: Unsupervised Dual Learning for Image-to-Image Translation
  18. alias 别名
  19. Codeforces Round #517 (Div. 2, based on Technocup 2019 Elimination Round 2)
  20. Spring Cloud Eureka 注册中心 服务消费者 服务提供者之间的关系以及高可用之间的联系

热门文章

  1. 安全关闭多Activity的Application
  2. Markdown常用基本语法
  3. 常见的HTTP 状态代码
  4. jquery attr()方法 添加,修改,获取对象的属性值。
  5. ycsb使用方法
  6. 【jQuery】: 定时刷新页面
  7. 解决ubuntu bash: cd: ~:Permission denied
  8. 360极速浏览器UA怪异以及如何用js判断360浏览器
  9. MVC_Ajax请求
  10. Mysql --分区表(2)