这次会抽时间把HttpClient访问Https链接的内容大致分为三部分来写,第一部分是由于不安全的证书导致的集中问题解决;第二部分将会叙述怎么实现单向SSL验证;第三部分则会叙述如何实现Https的双向认证。

一般情况下,我们通过

HttpClient client = new DefaultHttpClient();

得到的HttpClient对象是可以直接发起Https的访问的,但是如果服务端的SSL证书是不可信的(可能是证书应用范围不对,也可能是自签名的证书),那么可能会有这些错误

javax.net.ssl.SSLPeerUnverifiedException: No peer certificate
或者
javax.net.ssl.SSLException: hostname in certificate didn't match:
这部分我们要解决的办法就是通过接受所有服务端证书

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
/**
* 获取HttpClient
*
* @return
*/
public static DefaultHttpClient getNewHttpClient() {
try {
KeyStore trustStore = null;
SSLSocketFactory sf = null;
trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
trustStore.load(null, null);
sf = new EasySSLSocketFactory(trustStore);
sf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
Log.d("Cer Debug", "Allow All Hostname Verifier");

HttpParams params = new BasicHttpParams();
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
SchemeRegistry registry = new SchemeRegistry();
registry.register(new Scheme("http", PlainSocketFactory
.getSocketFactory(), 80));
registry.register(new Scheme("https", sf, 443));
ClientConnectionManager ccm = new ThreadSafeClientConnManager(
params, registry);
return new DefaultHttpClient(ccm, params);
} catch (Exception e) {
return new DefaultHttpClient();
}
}

EasySSLSocketFactory这个类很简单

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
package com.ilovn.app.httpsdemo;

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.conn.ssl.SSLSocketFactory;

import android.util.Log;

/**
* 处理HTTPS
* 接受所有
*
* @author Yong Zhao
*
*/
public class EasySSLSocketFactory extends SSLSocketFactory {
SSLContext sslContext = SSLContext.getInstance("TLS");

public EasySSLSocketFactory(KeyStore truststore)
throws NoSuchAlgorithmException, KeyManagementException,
KeyStoreException, UnrecoverableKeyException {
super(truststore);
TrustManager tm = new X509TrustManager() {
public void checkClientTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
}

public void checkServerTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
Log.d("SSL", "got X509 certificate from server:");
for (int i = 0; i < chain.length; i++) {
Log.d("SSL", "chain[" + i + "] issuer: "
+ chain[i].getIssuerDN().getName());
Log.d("SSL", "chain[" + i + "] subject: "
+ chain[i].getSubjectDN().getName());
}
}

public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
sslContext.init(null, new TrustManager[] { tm }, null);
}

@Override
public Socket createSocket(Socket socket, String host, int port,
boolean autoClose) throws IOException, UnknownHostException {
return sslContext.getSocketFactory().createSocket(socket, host, port,
autoClose);
}

@Override
public Socket createSocket() throws IOException {
return sslContext.getSocketFactory().createSocket();
}
}

调用的话也是很简单的

1
2
3
4
5
6
7
8
9
10
HttpClient client = HttpClientUtil.getNewHttpClient();
HttpGet get = new HttpGet("https://unicom.ilovn.com/");
try {
HttpResponse response = client.execute(get);
System.out.println(EntityUtils.toString(response.getEntity()));
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

这里主要就是接受了所有的SSL证书,在于安全来说,那还要SSL干嘛呢。下一篇我们就会来看如何实现客户端验证服务端的SSL证书。