android 让webview支持https 双向认证(SSL)

前言:

  最近完成一个项目,安全级别比较高。所以涉及到https双向认证,在网上找了很多资料都没有完美的解决方案。最后参考了org.sandrob.sslexample的实现方式,结合实际情况才完成该技术难题,现在分享一下我的实现方案来弥补这方面的空白。


正文:

1.android 4.0(不包含)以下版本的实现方法:

1.1 书写认证

private SSLContext createSSLContext() {
        SSLContext localSSLContext = null;
	try {
		// 创建一个证书库,并将证书导入证书库
		KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
		keyStore.load(
				mContext.getResources().openRawResource(R.raw.client),//client 是*.pfx文件
				CERTFILE_PASSWORD.toCharArray());//CERTFILE_PASSWORD 为你的证书的密码
		KeyManagerFactory localKeyManagerFactory = KeyManagerFactory
				.getInstance(KeyManagerFactory.getDefaultAlgorithm());
		localKeyManagerFactory.init(keyStore,
				CERTFILE_PASSWORD.toCharArray());
		KeyManager[] arrayOfKeyManager = localKeyManagerFactory
				.getKeyManagers();
		localSSLContext = SSLContext.getInstance("TLS");
		localSSLContext.init(arrayOfKeyManager, trustAllCerts,
				new SecureRandom());			
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return localSSLContext;
	}


/**
	 * 设置webview的ssl双向认证
	 * 注意:改方法只支持android4.0(不包含)一下
	 * 该方法调用一次即可
	 * 

Author : mingli

*

Date : 2013-7-2

*/ public boolean setWebViewSSLCert() { boolean issuc = false;// true 代表验证和设置成功 if (Build.VERSION.SDK_INT >= 14){ return issuc; } try { Field[] arrayOfField = Class.forName( "android.net.http.HttpsConnection").getDeclaredFields(); for (Field localField : arrayOfField) { if (localField.getName().equals("mSslSocketFactory")) {//采用反射的方式修改mSslSocketFactory变量 localField.setAccessible(true); localField.set(null,createSSLContext().getSocketFactory()); issuc = true; break; } } } catch (Exception ex) { ex.printStackTrace(); } return issuc; }


1.2 调用

        在webview初始化或者application 等,需要用https认证的地方调用 setWebViewSSLCert方法即可。

2.android 4.0(包含)以上版本的实现方法:

2.1 书写认证

private X509Certificate[] mX509Certificates;
private PrivateKey mPrivateKey;	
private void initPrivateKeyAndX509Certificate()
	throws Exception {
        KeyStore keyStore;
 // 创建一个证书库,并将证书导入证书库
KeyStore keyStore = KeyStore.getInstance("PKCS12", "BC");
keyStore.load(
mContext.getResources().openRawResource(R.raw.client),
CERTFILE_PASSWORD.toCharArray());
	Enumeration localEnumeration;
	localEnumeration = keyStore.aliases();
	while (localEnumeration.hasMoreElements()) {
			String str3 = (String) localEnumeration.nextElement();
			mPrivateKey = (PrivateKey) keyStore.getKey(str3,
					CERTFILE_PASSWORD.toCharArray());
			if (mPrivateKey == null) {
				continue;
			} else {
				Certificate[] arrayOfCertificate = keyStore
						.getCertificateChain(str3);
				mX509Certificates = new X509Certificate[arrayOfCertificate.length];
				for (int j = 0; j < mX509Certificates.length; j++) {
					mX509Certificates[j] = ((X509Certificate) arrayOfCertificate[j]);
				}
			}
		}
	}


public class BasicWebViewClientEx extends WebViewClient {	


	private X509Certificate[] certificatesChain;
	private PrivateKey clientCertPrivateKey;
	  
	public BasicWebViewClientEx(AbstractActivity activity) {
		mActivity = activity;
		certificatesChain = getX509Certificates();//此处就是上文中的mX509Certificates
		clientCertPrivateKey = getPrivateKey();//次处就是上文中的mPrivateKey
	}
	
	public void onReceivedClientCertRequest(WebView view,
			ClientCertRequestHandler handler, String host_and_port) {
                //注意该方法是调用的隐藏函数接口。这儿是整个验证的技术难点:就是如何调用隐藏类的接口。
                //方法:去下载一个android4.2版本全编译后的class.jar 然后导入到工程中
		if((null != clientCertPrivateKey) && ((null!=certificatesChain) && (certificatesChain.length !=0))){
			handler.proceed(this.clientCertPrivateKey, this.certificatesChain);	
		}else{
			handler.cancel();
		}		
	}


		
	@Override
	public void onReceivedSslError(final WebView view, SslErrorHandler handler,
			SslError error) {		
		handler.proceed();	
	}
	
}

2.2 调用

mWebView.setWebViewClient(new BasicWebViewClientEx());

2.3 编译

方案一:到android 4.2 源码环境下编译
方案二:(推荐)


1.去下载一个全编译的class.jar(http://download.csdn.net/detail/sfhong2008/5506219)
2.把该class.jar导入工程。步骤如下:(http://www.linuxidc.com/Linux/2012-02/54978.htm)
使用Eclipse,Android工程添加library(BuildPath -> Add Libraries->User Library->New User Library),将.jar文件加入添加到library,同时勾选“SystemLibrary”选项,www.linuxidc.com 以避免产生“java.lang.OutOfMemoryError:Java Heap Space”错误。如果已经正确导入了jar库,却仍然找不到隐藏的API。原因可能是Buildclass path order不正确,即android.jar和classes.jar的导入顺序不对,具体调节Buildclass path order,选择Build Path-> Config Build Path->Order and Export,调整自定义的library与android.jar的顺序。
3.编译


后记:
    分享完毕,欢迎大家拍砖和提问哦。共同进步……


你可能感兴趣的:(android)