参考okhttp调用https实现自签名SSL证书:
stackoverflow:How to use Ksoap2 on ssl (https) connection - Android
http://stackoverflow.com/questions/28583614/how-to-use-ksoap2-on-ssl-https-connection-android
我的项目中使用网络请求库是webservice方式实现,依赖包版本是ksoap2-android-assembly-3.3.0-jar-with-dependencies.jar。
证书验证类
SslRequest.java如下:
package com.tcps.xiangyangtravel.network;
import android.annotation.SuppressLint;
import android.content.Context;
import android.renderscript.ScriptGroup.Input;
import android.util.Log;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
import com.tcps.xiangyangpassenger.activity.NetMainActivity;
import com.tcps.xiangyangtravel.activity.Splash;
import com.tcps.xiangyangtravel.app.BusApplication;
import com.tcps.xiangyangtravel.utils.LogUtils;
/**
* Created by Sanjay Kumar on 2/18/2015.
* Copy by Liying 12/2016
*/
public class SslRequest implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new
X509Certificate[]{};
@Override
public void checkClientTrusted(X509Certificate[] chain, String
authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String
authType) throws CertificateException {
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
@SuppressLint("TrulyRandom")
public static void allowAllSSL(String url,Context mContext) {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
SSLContext context = null;
InputStream inputStream = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new SslRequest()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
try {
//自签名证书client.cer放在assets文件夹下
inputStream = mContext.getAssets().open("client.cer");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
SSLSocketFactory socketFactory = SSLUtil.getSSLSocketFactory(inputStream);
if (socketFactory != null) {
HttpsURLConnection.setDefaultSSLSocketFactory(socketFactory);
}
/*
* 使用此方法并配合HttpsURLConnection connection设置(见下面)则可以越过证书验证
*/
// HttpsURLConnection.setDefaultSSLSocketFactory(context.getSocketFactory());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
LogUtils.e("Ssl_NoSuchAlgorithmException&&&&&&&&&&&>>>>>>>>>", e.toString());
} catch (KeyManagementException e) {
e.printStackTrace();
LogUtils.e("Ssl_KeyManagementException&&&&&&&&&&&>>>>>>>>>", e.toString());
}
/*
* 使用此方法则可以越过证书验证
*/
/*HttpsURLConnection connection;
try {
connection = (HttpsURLConnection) new URL(url).openConnection();
((HttpsURLConnection) connection).setHostnameVerifier(new AllowAllHostnameVerifier());
connection.setUseCaches(false);
connection.setDoOutput(true);
connection.setDoInput(true);
} catch (MalformedURLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
LogUtils.e("Ssl_MalformedURLException&&&&&&&&&&&>>>>>>>>>", e.toString());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
LogUtils.e("Ssl_IOException&&&&&&&&&&&>>>>>>>>>", e.toString());
}*/
}
}
SSLUtil.java如下
(来源:篇首CSDN博客提及https://github.com/lizhangqu/CoreUtil):
package com.tcps.xiangyangtravel.network;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManagerFactory;
/**
* Https 证书工具类
* User:lizhangqu(513163535@qq.com)
* Date:2015-09-02
* Time: 12:52
*/
public class SSLUtil {
//使用命令keytool -printcert -rfc -file srca.cer 导出证书为字符串,然后将字符串转换为输入流,如果使用的是okhttp可以直接使用new Buffer().writeUtf8(s).inputStream()
/**
* 返回SSLSocketFactory
*
* @param certificates 证书的输入流
* @return SSLSocketFactory
*/
public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) {
return getSSLSocketFactory(null,certificates);
}
/**
* 双向认证
* @param keyManagers KeyManager[]
* @param certificates 证书的输入流
* @return SSLSocketFactory
*/
public static SSLSocketFactory getSSLSocketFactory(KeyManager[] keyManagers, InputStream... certificates) {
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e) {
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
sslContext.init(keyManagers, trustManagerFactory.getTrustManagers(), new SecureRandom());
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
return socketFactory;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获得双向认证所需的参数
* @param bks bks证书的输入流
* @param keystorePass 秘钥
* @return KeyManager[]对象
*/
public static KeyManager[] getKeyManagers(InputStream bks, String keystorePass) {
KeyStore clientKeyStore = null;
try {
clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bks, keystorePass.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, keystorePass.toCharArray());
KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
return keyManagers;
} catch (KeyStoreException e) {
e.printStackTrace();
} catch (UnrecoverableKeyException e) {
e.printStackTrace();
} catch (CertificateException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
在发出https请求前调用一次SslRequest.allowAllSSL(“”,context);证书验证(可以写在程序的第一个Activity中),验证成功后,后续调用https请求即可。
由于我的项目使用的是ksoap框架调用webservice,之前用的是http请求:
public final static String WEBSERVICE_BUS_URL = "http://180.89.58.20:xxxx/bus_service_new/ServicesFacadePort?wsdl";(url类型)
public final static String BUS_SERVICE_NAMESPACE = "http://xxx.xxxx.com.cn/";(命名空间)
String method = "getSystem";(方法名)
private static HttpTransportSE transport;
transport = new HttpTransportSE(AppUtil.WEBSERVICE_BUS_URL, AppUtil.TIMEOUT);
---------------------------------envelope----------
String url;
String serviceNamespace;
url = AppUtil.WEBSERVICE_BUS_URL;
serviceNamespace = AppUtil.BUS_SERVICE_NAMESPACE;
SoapObject request = new SoapObject(serviceNamespace, method);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
Element el = new Element().createElement(serviceNamespace, "TCPSHeader");
el.setAttribute(serviceNamespace, "accessType", AppUtil.ACCESSTYPE);(可以用于区分ios和android,规则自己定,比如AppUtil.ACCESSTYPE = “1”代表android)
el.setAttribute(serviceNamespace, "productID", AppUtil.PRODUCTID);(唯一)
el.setAttribute(serviceNamespace, "version", AppUtil.VERSION);(apk版本号,只是便于记录,可以随意)
header[0] = el;
envelope.headerOut = header;
envelope.dotNet = false;
//这里可以加密传参
if (param != null) {
for (int n = 0; n < param.length; n++) {
request.addProperty("arg" + n, param[n]);
}
}
envelope.bodyOut = request;
---------------------------------envelope----------
//base64.register(envelope);(加密注册)
Log.i("调用时间-----------------------", String.valueOf(System.currentTimeMillis()));
try {
transport.call(url, envelope);
System.out.println(
"-----------------" + "请求的数据" + request.toString() + "返回: " + envelope.getResponse().toString());(返回数据也可以加密,在需要的地方再解密)
return envelope.getResponse();
} catch (Exception e) {
if (e instanceof SocketTimeoutException) {
return R.string.error_timeout;(超时)
} else {
return R.string.error_unknown;(未知错误)
}
}
改为https请求后,只需要将HttpTransportSE换成HttpsTransportSE,然后:
public final static String WEBSERVICE_BUS_URL = "http://180.89.58.20:xxxx/bus_service_new/ServicesFacadePort?wsdl";(url类型)
public final static String BUS_SERVICE_NAMESPACE = "http://xxx.xxxx.com.cn/";(命名空间)
String nameSpace = "";
String URL = "";
String HOST = "";
String WS_OPS = "";
String SOAP_ACTION = "";
String METHOD_NAME = method;
Integer POST = 0;
if (URL.startsWith("https")){
/*
* https请求
*/
nameSpace = AppUtil.BUS_SERVICE_NAMESPACE;
URL = AppUtil.WEBSERVICE_BUS_URL;
HOST = "180.89.58.20";
WS_OPS = "/bus_service_new/ServicesFacadePort?wsdl";(指AppUtil.WEBSERVICE_BUS_URL去掉HOST和POST后的部分)
SOAP_ACTION = nameSpace + method;
POST = xxxx;
}
HttpsTransportSE androidHttpTransport = new HttpsTransportSE(HOST,POST,WS_OPS,AppUtil.TIMEOUT);
androidHttpTransport.debug = true;
//去掉ssl证书验证
// SslRequest.allowAllSSL(URL,mContext);(在程序每次启动时已调用,就不用每次调用接口时去重复验证了)
try {
androidHttpTransport.call(SOAP_ACTION, envelope);(envelope不变,参照上面的http请求方式)
if (envelope.getResponse() != null) {
result_return = envelope.getResponse().toString();
LogUtils.e("--------------------------------", "返回结果" + result_return);
}
} catch (Exception e) {
if (e instanceof SocketTimeoutException) {
result_return = String.valueOf(R.string.error_timeout);
} else {
result_return = String.valueOf(R.string.error_unknown);
}
}
主要是多了一步证书验证。客户端请求类由http换成https类型,方法都封装在那个ksoap包中。
附录:
1.HTTPS和HTTP的区别
http://blog.csdn.net/whatday/article/details/38147103
https误解,by阮一峰
http://www.ruanyifeng.com/blog/2011/02/seven_myths_about_https.html
2.中间人攻击(Man-in-the-MiddleAttack,简称“MITM攻击”)
http://baike.baidu.com/link?url=B6dlrSMB_zDpK9j7adXavqwi0hC6Ql24kgC0tUPS8PoZ6IDrHfzSt0G0zmrHRoXBJRSjDL3yKEFeyPTYILYvEmeFCFY1-MkNsVxEerHEcKAhz3oVz-ruWQcES7YBoufdiA14ONbEYlDFRRTeXVSkfK
3.张鸿洋 Android Https相关完全解析 当OkHttp遇到Https,我的天他参考可那么多资料
http://blog.csdn.net/lmj623565791/article/details/48129405