最近项目需要用到httpClient走HTTPS的访问方式进行数据传输,在度娘与谷哥大神的帮助下,结论出了以下做法,提出来给需要的人,技术上就不多说了自己查了
环境:android 4.4 api 19
帮助:
1.低版本下(4.x或更低)开放TLSv1.1/TLSv1.2功能
2.解决net.ssl.SSLPeerUnverifiedException: No peer certificate...
1.创建class
importorg.apache.http.HttpVersion;
importorg.apache.http.conn.ClientConnectionManager;
importorg.apache.http.conn.params.ConnManagerPNames;
importorg.apache.http.conn.params.ConnPerRouteBean;
importorg.apache.http.conn.scheme.PlainSocketFactory;
importorg.apache.http.conn.scheme.Scheme;
importorg.apache.http.conn.scheme.SchemeRegistry;
importorg.apache.http.conn.ssl.SSLSocketFactory;
importorg.apache.http.impl.client.DefaultHttpClient;
importorg.apache.http.impl.conn.SingleClientConnManager;
importorg.apache.http.params.BasicHttpParams;
importorg.apache.http.params.HttpConnectionParams;
importorg.apache.http.params.HttpParams;
importorg.apache.http.params.HttpProtocolParams;
importorg.apache.http.protocol.HTTP;
importjava.io.IOException;
importjava.net.Socket;
importjava.net.UnknownHostException;
importjava.security.KeyManagementException;
importjava.security.KeyStore;
importjava.security.KeyStoreException;
importjava.security.NoSuchAlgorithmException;
importjava.security.UnrecoverableKeyException;
importjava.security.cert.X509Certificate;
importjavax.net.ssl.SSLContext;
importjavax.net.ssl.SSLSocket;
importjavax.net.ssl.TrustManager;
importjavax.net.ssl.X509TrustManager;
publicclass SSLSocketFactoryEx extends SSLSocketFactory {
public final static int TIME_OUT_DELAY =20000;
SSLContext sslContext =SSLContext.getInstance("TLS");
public SSLSocketFactoryEx(KeyStoretruststore)
throws NoSuchAlgorithmException,KeyManagementException,
KeyStoreException,UnrecoverableKeyException {
super(truststore);
initSSLContext();
}
TrustManager tm = new X509TrustManager() {
@Override
public X509Certificate[]getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
}
@Override
public void checkClientTrusted(
java.security.cert.X509Certificate[]chain, String authType)
throwsjava.security.cert.CertificateException {}
@Override
public void checkServerTrusted(
java.security.cert.X509Certificate[] chain, String authType)
throwsjava.security.cert.CertificateException {}
};
@Override
public Socket createSocket(Socket socket,String host, int port, boolean autoClose) throws IOException,UnknownHostException {
SSLSocket sslSocket=(SSLSocket)sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
sslSocket.setEnabledProtocols(newString[]{"TLSv1.2"});
return sslSocket;
}
private void initSSLContext() {
try {
// 既然是跳过认证,我们把没有的都填null,此时发现第二个参数是一个数组,那么意思就是我们可以放多个证书认证;
sslContext.init(null, newTrustManager[] { tm }, null);
} catch (KeyManagementException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static DefaultHttpClientgetNewHttpClient() {
try {
// 查看API这里可以得到一个默认的Type.
KeyStore truststore =KeyStore.getInstance(KeyStore
.getDefaultType());
truststore.load(null, null);
// 这里发现需要一个KeyStore,那么我们就在上面New一个KeyStore,这是一个密钥库,查看API发现能直接getInstance得到对象;
SSLSocketFactory factory = newSSLSocketFactoryEx(truststore);
// 这里就是我们最需要的也是最关键的一步,设置主机认证,通过API发现有一个常量就是允许所有认证通过。
factory.setHostnameVerifier(ALLOW_ALL_HOSTNAME_VERIFIER);
// 实现Httpprams的子类
HttpParams params = newBasicHttpParams();
params.setParameter(ConnManagerPNames.MAX_TOTAL_CONNECTIONS,30);
params.setParameter(ConnManagerPNames.MAX_CONNECTIONS_PER_ROUTE, newConnPerRouteBean(30));
params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, false);
//通过Http适配器设置必要参数,现在通用HTTP1.1协议,和UTF-8字符。
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
HttpProtocolParams.setContentCharset(params, HTTP.UTF_8);
//通过适配器设置连接参数,等待时间和,连接时间
HttpConnectionParams.setSoTimeout(params, TIME_OUT_DELAY);
HttpConnectionParams.setConnectionTimeout(params, TIME_OUT_DELAY);
// 同样New出来,查看API,需要我们注册一个计划,来封装协议细节
SchemeRegistry schreg = newSchemeRegistry();
// 最后一个参数是端口设置,Https常用的端口就是443。
schreg.register(newScheme("https", factory, 443));
// 既然是工具类,这里也把http协议加上,中间的协议工厂我们就用简单的PlainSocketFactory,这里可以通过API查看到,端口就用常用的80端口(默认)
schreg.register(newScheme("http", PlainSocketFactory.getSocketFactory(), 80));
// ClientConnectionManager是一个借口,就实现他的子类,同样需要2个参数,第一个我们熟悉,第二个就是让我们自定义自己的一套方案协议,继续在上面一步一步完成;
ClientConnectionManager conman =new SingleClientConnManager(
params, schreg);
// 返回我们需要的一个默认Httpclient,为了把之前做的关联起来,就new最多参数的构造函数,需要2个参数,Httpparams是我们熟悉的,
// 发现ClientConnectionManager不太熟悉,通过API发现这是客服端连接管理者,既然这样,就在上面一步一步完成。
return newDefaultHttpClient(conman, params);
} catch (Exception e) {
// TODO: handle exception
}
return new DefaultHttpClient();
}
}
2.应用上
//for https
HttpClient httpClient =SSLSocketFactoryEx.getNewHttpClient();
//設定參數
httpClient.getParams().setIntParameter(HttpConnectionParams.SO_TIMEOUT,TIME_OUT_DELAY);
httpClient.getParams().setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT,TIME_OUT_DELAY);
HttpPost httpPost = new HttpPost(EndpointURL);
//添加http头信息
httpPost.addHeader("xxx...",xxx...);
//json數據
StringEntitystringEntity = new StringEntity(jsonString);
stringEntity.setContentType("application/json;charset=UTF-8");
stringEntity.setContentEncoding(newBasicHeader(HTTP.CONTENT_TYPE,"application/json;charset=UTF-8"));
httpPost.setEntity(stringEntity);
//回傳服務查詢結果(json)
httpResponse =httpClient.execute(httpPost);
Good luck.