您的位置:首页 > 理论基础 > 计算机网络

Https

2013-11-21 11:45 190 查看


问题:

项目中Android https或http请求地址重定向为HTTPS的地址,相信很多人都遇到了这个异常(无终端认证):

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate


解决过程:


1.没遇到过的问题,搜索吧,少年

log里出现这个异常,作者第一次遇到,不知道啥意思。看下字面意思,是ssl协议中没有终端认证。SSL?

作者没用到ssl协议呀,只是通过httpClient请求一个重定向https的地址。

好吧,google下,知道了个差不多情况的帖子,http://www.eoeandroid.com/thread-161747-1-1.html。恩恩,一个不错的帖子,给出了个解决方案。照着来试下。添加个继承SSLSocketFactory的

自定义类。并在初始化httpclient支持https时,注册进去。看下面代码:

01
public
class
HttpClientHelper
{
02
03
private
static
HttpClient
httpClient;
04
05
private
HttpClientHelper()
{
06
}
07
08
public
static
synchronized
HttpClient
getHttpClient() {
09
10
if
(
null
==
httpClient) {
11
//
初始化工作
12
try
{
13
KeyStore
trustStore = KeyStore.getInstance(KeyStore
14
.getDefaultType());
15
trustStore.load(
null
,
null
);
16
SSLSocketFactory
sf =
new
SSLSocketFactoryEx(trustStore);
17
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
//允许所有主机的验证
18
19
HttpParams
params =
new
BasicHttpParams();
20
21
HttpProtocolParams.setVersion(params,
HttpVersion.HTTP_1_1);
22
HttpProtocolParams.setContentCharset(params,
23
HTTP.DEFAULT_CONTENT_CHARSET);
24
HttpProtocolParams.setUseExpectContinue(params,
true
);
25
26
//
设置连接管理器的超时
27
ConnManagerParams.setTimeout(params,
10000
);
28
//
设置连接超时
29
HttpConnectionParams.setConnectionTimeout(params,
10000
);
30
//
设置socket超时
31
HttpConnectionParams.setSoTimeout(params,
10000
);
32
33
//
设置http https支持
34
SchemeRegistry
schReg =
new
SchemeRegistry();
35
schReg.register(
new
Scheme(
"http"
,
PlainSocketFactory
36
.getSocketFactory(),
80
));
37
schReg.register(
new
Scheme(
"https"
,
sf,
443
));
38
39
ClientConnectionManager
conManager =
new
ThreadSafeClientConnManager(
40
params,
schReg);
41
42
httpClient
=
new
DefaultHttpClient(conManager,
params);
43
}
catch
(Exception
e) {
44
e.printStackTrace();
45
return
new
DefaultHttpClient();
46
}
47
}
48
return
httpClient;
49
}
50
51
}
52
53
class
SSLSocketFactoryEx
extends
SSLSocketFactory
{
54
55
SSLContext
sslContext = SSLContext.getInstance(
"TLS"
);
56
57
public
SSLSocketFactoryEx(KeyStore
truststore)
58
throws
NoSuchAlgorithmException,
KeyManagementException,
59
KeyStoreException,
UnrecoverableKeyException {
60
super
(truststore);
61
62
TrustManager
tm =
new
X509TrustManager()
{
63
64
@Override
65
public
java.security.cert.X509Certificate[]
getAcceptedIssuers() {
66
return
null
;
67
}
68
69
@Override
70
public
void
checkClientTrusted(
71
java.security.cert.X509Certificate[]
chain,String authType)
72
throws
java.security.cert.CertificateException
{
73
74
}
75
76
@Override
77
public
void
checkServerTrusted(
78
java.security.cert.X509Certificate[]
chain,String authType)
79
throws
java.security.cert.CertificateException
{
80
81
}
82
};
83
84
sslContext.init(
null
,
new
TrustManager[]
{ tm },
null
);
85
}
86
87
@Override
88
public
Socket
createSocket(Socket socket,String host,
int
port,
89
boolean
autoClose)
throws
IOException,
UnknownHostException {
90
return
sslContext.getSocketFactory().createSocket(socket,
host,port,
91
autoClose);
92
}
93
94
@Override
95
public
Socket
createSocket()
throws
IOException
{
96
return
sslContext.getSocketFactory().createSocket();
97
}
98
}
ok,run下,狂乱的点到测试按钮,深吸口气,盯着eclipse中的logat。咦?神奇的竟然没有报之前的 javax.net.ssl.SSLPeerUnverifiedException: No peer
certificate的异常了。服务端的数据正常返回了。

,狂喜中...


2.了解并分析问题

狂喜中,得分析这问题诶。不然老大来问,啥情况?楞半天不知道咋说(

作者就经常这样,所以吸取教训。所以的弄懂出现的问题,学习+汇报工作)。

思来想去,就是作者请求的是一个重定向https的地址。好吧,那就学习下https(之前被老大深深的教过,http就是request/response)。继续搜索吧,少年。下面总结下学习到的https知识。


2.1 https

HTTPS:超文本安全传输协议,和HTTP相比,多了一个SSL/TSL的认证过程,端口为443。(鄙视下之前说的)

作者没用到ssl协议呀,只是通过httpClient请求一个重定向https的地址

1.peer终端发送一个request,https服务端把支持的加密算法等以证书的形式返回一个身份信息(包含ca颁发机构和加密公钥等)。

2.获取证书之后,验证证书合法性。

3.随机产生一个密钥,并以证书当中的公钥加密。

4.request https服务端,把用公钥加密过的密钥传送给https服务端。

5.https服务端用自己的密钥解密,获取随机值。

6.之后双方传送数据都用此密钥加密后通信。
看下面一张网上的得来的https的时序图:






2.2分析下出现问题的原因

好吧,大概的流程知道了。定位已经非常清楚了。在第2步验证证书时,无法验证。为啥无法验证呢?没有添加信任。详细参考下

/article/5007759.html讲的非常清楚https-ssl的认证过程,膜拜下该作者



这样想来,上面提供的解决方案就是添加默认信任全部证书。以此来通过接下来的通信。


3.解决问题

但是,这样问题是解决了。但是觉得还是不带靠谱(信任全部证书

有点危险)。继续噼噼啪啪的网上搜索一番。又找到了一种解决方案,其过程大致这样的:

1.浏览器访问https地址,保存提示的证书到本地,放到android项目中的assets目录。

2.导入证书,代码如下。

3.把证书添加为信任。

01
String
requestHTTPSPage(String mUrl) {
02
InputStream
ins =
null
;
03
String
result =
""
;
04
try
{
05
ins
= context.getAssets().open(
"app_pay.cer"
);
//下载的证书放到项目中的assets目录中
06
CertificateFactory
cerFactory = CertificateFactory
07
.getInstance(
"X.509"
);
08
Certificate
cer = cerFactory.generateCertificate(ins);
09
KeyStore
keyStore = KeyStore.getInstance(
"PKCS12"
,
"BC"
);
10
keyStore.load(
null
,
null
);
11
keyStore.setCertificateEntry(
"trust"
,
cer);
12
13
SSLSocketFactory
socketFactory =
new
SSLSocketFactory(keyStore);
14
Scheme
sch =
new
Scheme(
"https"
,
socketFactory,
443
);
15
HttpClient
mHttpClient =
new
DefaultHttpClient();
16
mHttpClient.getConnectionManager().getSchemeRegistry()
17
.register(sch);
18
19
BufferedReader
reader =
null
;
20
try
{
21
Log.d(TAG,
"executeGet
is in,murl:"
+
mUrl);
22
HttpGet
request =
new
HttpGet();
23
request.setURI(
new
URI(mUrl));
24
HttpResponse
response = mHttpClient.execute(request);
25
if
(response.getStatusLine().getStatusCode()
!=
200
)
{
26
request.abort();
27
return
result;
28
}
29
30
reader
=
new
BufferedReader(
new
InputStreamReader(response
31
.getEntity().getContent()));
32
StringBuffer
buffer =
new
StringBuffer();
33
String
line =
null
;
34
while
((line
= reader.readLine()) !=
null
)
{
35
buffer.append(line);
36
}
37
result
= buffer.toString();
38
Log.d(TAG,
"mUrl="
+
mUrl +
"\nresult
= "
+
result);
39
}
catch
(Exception
e) {
40
e.printStackTrace();
41
}
finally
{
42
if
(reader
!=
null
)
{
43
reader.close();
44
}
45
}
46
}
catch
(Exception
e) {
47
//
TODO: handle exception
48
}
finally
{
49
try
{
50
if
(ins
!=
null
)
51
ins.close();
52
}
catch
(IOException
e) {
53
e.printStackTrace();
54
}
55
}
56
return
result;
57
}
接着,验证下呗。吼吼,稀里糊涂的又可以了。感动的泪流满面。


最后总结:

2种方法都解决了作者遇到的问题,这里记录下。以防下次遇到,希望能给遇到相同问题朋友有所参考帮助
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: