android中对部分站点发送https请求会报错,原因是该站点的证书时自定义的,而非官方的,android手机不信任其证书,为了解决这个问题,一般有两种解决方案
在android中我们访问网络一般有两种方式,一种是使用httpURLConnection,一种是使用httpClient。这里先讲前者,然后再讲解后者。顺便会讲解在volley框架中如何使用https。
一,HttpURLConnection访问https站点
1,忽略证书
如果要忽略证书的验证,我们只需提供一个HostnameVerifier和X509TrustManager的空实现,如下代码
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 public void requestWithoutCA () {
try {
SSLContext sc = SSLContext . getInstance ( "TLS" );
sc . init ( null , new TrustManager [] { new MyTrustManager () },
new SecureRandom ());
HttpsURLConnection
. setDefaultSSLSocketFactory ( sc . getSocketFactory ());
HttpsURLConnection
. setDefaultHostnameVerifier ( new MyHostnameVerifier ());
URL url = new URL ( "https://certs.cac.washington.edu/CAtest/" );
HttpURLConnection urlConnection = ( HttpURLConnection ) url
. openConnection ();
InputStream in = urlConnection . getInputStream ();
// 取得输入流,并使用Reader读取
BufferedReader reader = new BufferedReader (
new InputStreamReader ( in ));
System . out . println ( "=============================" );
System . out . println ( "Contents of get request" );
System . out . println ( "=============================" );
String lines ;
while (( lines = reader . readLine ()) != null ) {
System . out . println ( lines );
}
reader . close ();
// 断开连接
urlConnection . disconnect ();
System . out . println ( "=============================" );
System . out . println ( "Contents of get request ends" );
System . out . println ( "=============================" );
} catch ( MalformedURLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( IOException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( NoSuchAlgorithmException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( KeyManagementException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
private class MyHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify ( String hostname , SSLSession session ) {
// TODO Auto-generated method stub
return true ;
}
}
private class MyTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted ( X509Certificate [] chain , String authType )
throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public void checkServerTrusted ( X509Certificate [] chain , String authType )
throws CertificateException {
// TODO Auto-generated method stub
}
@Override
public X509Certificate [] getAcceptedIssuers () {
// TODO Auto-generated method stub
return null ;
}
}
2,使用证书
使用证书这部分我是参考于官网:
Security with HTTPS and SSL
该代码时访问华盛顿大学的一个网页,证书可以到以下链接下载:
https://www.washington.edu/itconnect/security/ca/load-der.crt
代码如下:
注意,此处使用的是HttpsURLConnection,而非HttpURLConnection。
下面的代码,加载证书等等操作,最终都是为了setSSLSocketFactory这一步。
public void requestByHttps () {
try {
// Load CAs from an InputStream
// (could be from a resource or ByteArrayInputStream or ...)
CertificateFactory cf = CertificateFactory . getInstance ( "X.509" );
// From
// https://www.washington.edu/itconnect/security/ca/load-der.crt
InputStream caInput = this . getAssets (). open ( "load_der.crt" );
Certificate ca ;
try {
ca = cf . generateCertificate ( caInput );
System . out . println ( "ca="
+ (( X509Certificate ) ca ). getSubjectDN ());
} finally {
caInput . close ();
}
// Create a KeyStore containing our trusted CAs
String keyStoreType = KeyStore . getDefaultType ();
KeyStore keyStore = KeyStore . getInstance ( keyStoreType );
keyStore . load ( null , null );
keyStore . setCertificateEntry ( "ca" , ca );
// Create a TrustManager that trusts the CAs in our KeyStore
String tmfAlgorithm = TrustManagerFactory . getDefaultAlgorithm ();
TrustManagerFactory tmf = TrustManagerFactory
. getInstance ( tmfAlgorithm );
tmf . init ( keyStore );
// Create an SSLContext that uses our TrustManager
SSLContext context = SSLContext . getInstance ( "TLS" );
context . init ( null , tmf . getTrustManagers (), null );
URL url = new URL ( "https://certs.cac.washington.edu/CAtest/" );
HttpsURLConnection urlConnection = ( HttpsURLConnection ) url
. openConnection ();
urlConnection . setSSLSocketFactory ( context . getSocketFactory ());
InputStream in = urlConnection . getInputStream ();
// 取得输入流,并使用Reader读取
BufferedReader reader = new BufferedReader (
new InputStreamReader ( in ));
System . out . println ( "=============================" );
System . out . println ( "Contents of get request" );
System . out . println ( "=============================" );
String lines ;
while (( lines = reader . readLine ()) != null ) {
System . out . println ( lines );
// tv.setText(tv.getText().toString() + lines);
}
reader . close ();
// 断开连接
urlConnection . disconnect ();
System . out . println ( "=============================" );
System . out . println ( "Contents of get request ends" );
System . out . println ( "=============================" );
} catch ( MalformedURLException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( IOException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( NoSuchAlgorithmException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( KeyManagementException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( KeyStoreException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
} catch ( CertificateException e ) {
// TODO Auto-generated catch block
e . printStackTrace ();
}
}
3, Volley中使用HttpURLConnection访问https
Volley中使用HttpURLConnection访问网络时,是由HurlStack类处理,该类的几个构造方法如下:
public HurlStack () {
this ( null );
}
/**
* @param urlRewriter Rewriter to use for request URLs
*/
public HurlStack ( UrlRewriter urlRewriter ) {
this ( urlRewriter , null );
}
/**
* @param urlRewriter Rewriter to use for request URLs
* @param sslSocketFactory SSL factory to use for HTTPS connections
*/
public HurlStack ( UrlRewriter urlRewriter , SSLSocketFactory sslSocketFactory ) {
mUrlRewriter = urlRewriter ;
mSslSocketFactory = sslSocketFactory ;
}
打开连接的方法如下,注意其中也有上面提到的的setSSLSocketFactory方法。
结合构造方法以及以下的代码,不难理解,为了访问https,只需奥在构建HurlStack时,传入一个已经设置了证书的SLSocketFactory即可,设置代码和上面的代码类似。
private HttpURLConnection openConnection ( URL url , Request <?> request ) throws IOException {
HttpURLConnection connection = createConnection ( url );
int timeoutMs = request . getTimeoutMs ();
connection . setConnectTimeout ( timeoutMs );
connection . setReadTimeout ( timeoutMs );
connection . setUseCaches ( false );
connection . setDoInput ( true );
// use caller-provided custom SslSocketFactory, if any, for HTTPS
if ( "https" . equals ( url . getProtocol ()) && mSslSocketFactory != null ) {
(( HttpsURLConnection ) connection ). setSSLSocketFactory ( mSslSocketFactory );
}
return connection ;
}
二,使用httpClient访问https站点
1,忽略证书
public class HttpClientHelper {
private static HttpClient httpClient ;
private HttpClientHelper () {
}
public static synchronized HttpClient getHttpClient () {
if ( null == httpClient ) {
// 初始化工作
try {
KeyStore trustStore = KeyStore . getInstance ( KeyStore
. getDefaultType ());
trustStore . load ( null , null );
SSLSocketFactory sf = new SSLSocketFactoryEx ( trustStore );
sf . setHostnameVerifier ( SSLSocketFactory . ALLOW_ALL_HOSTNAME_VERIFIER ); //允许所有主机的验证
HttpParams params = new BasicHttpParams ();
HttpProtocolParams . setVersion ( params , HttpVersion . HTTP_1_1 );
HttpProtocolParams . setContentCharset ( params ,
HTTP . DEFAULT_CONTENT_CHARSET );
HttpProtocolParams . setUseExpectContinue ( params , true );
// 设置连接管理器的超时
ConnManagerParams . setTimeout ( params , 10000 );
// 设置连接超时
HttpConnectionParams . setConnectionTimeout ( params , 10000 );
// 设置socket超时
HttpConnectionParams . setSoTimeout ( params , 10000 );
// 设置http https支持
SchemeRegistry schReg = new SchemeRegistry ();
schReg . register ( new Scheme ( "http" , PlainSocketFactory
. getSocketFactory (), 80 ));
schReg . register ( new Scheme ( "https" , sf , 443 ));
ClientConnectionManager conManager = new ThreadSafeClientConnManager (
params , schReg );
httpClient = new DefaultHttpClient ( conManager , params );
} catch ( Exception e ) {
e . printStackTrace ();
return new DefaultHttpClient ();
}
}
return httpClient ;
}
}
class SSLSocketFactoryEx extends SSLSocketFactory {
SSLContext sslContext = SSLContext . getInstance ( "TLS" );
public SSLSocketFactoryEx ( KeyStore truststore )
throws NoSuchAlgorithmException , KeyManagementException ,
KeyStoreException , UnrecoverableKeyException {
super ( truststore );
TrustManager tm = new X509TrustManager () {
@Override
public java . security . cert . X509Certificate [] getAcceptedIssuers () {
return null ;
}
@Override
public void checkClientTrusted (
java . security . cert . X509Certificate [] chain , String authType )
throws java . security . cert . CertificateException {
}
@Override
public void checkServerTrusted (
java . security . cert . X509Certificate [] chain , String authType )
throws java . security . cert . CertificateException {
}
};
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 ();
}
}
2,使用证书
AssetManager am = context . getAssets ();
InputStream ins = am . open ( "robusoft.cer" );
try {
//读取证书
CertificateFactory cerFactory = CertificateFactory . getInstance ( "X.509" ); //问1
Certificate cer = cerFactory . generateCertificate ( ins );
//创建一个证书库,并将证书导入证书库
KeyStore keyStore = KeyStore . getInstance ( "PKCS12" , "BC" ); //问2
keyStore . load ( null , null );
keyStore . setCertificateEntry ( "trust" , cer );
return keyStore ;
} finally {
ins . close ();
}
//把咱的证书库作为信任证书库
SSLSocketFactory socketFactory = new SSLSocketFactory ( keystore );
Scheme sch = new Scheme ( "https" , socketFactory , 443 );
//完工
HttpClient mHttpClient = new DefaultHttpClient ();
mHttpClient . getConnectionManager (). getSchemeRegistry (). register ( sch );
//接下来这个httpclient就可以用语https请求了。
3,Volley中使用httpClient访问https
Volley使用HttpCLient访问网络是借助HttpClientStack类,其中的构造方法不同于HurlStack那么多,而只有一个
public HttpClientStack ( HttpClient client ) {
mClient = client ;
}
很简单,我们只需要在构造HttpClientStack时传入一个已经设置了证书的HttpClient,按照上面讲述的那样设置即可。