android ksoap2下webservice的 https请求
参考okhttp调用https实现自签名SSL证书:
http://www.cnblogs.com/tommylemon/p/5454303.html
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
更多相关文章
- android 获取SIM卡的类型··
- Android Studio 指定签名证书文件
- Android Studio获取SHA1证书方法
- Android EditText插入字符串到光标所在位置和删除
- Android将获取到文件的uri转换为字符串的路径
- JS C# 获取浏览器类型
- Android 创建一个包含星期的格式字符串