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(不包含)一下
	 * 該方法調用一次即可
	 * <P>Author : mingli </P>  
	 * <P>Date : 2013-7-2 </P>
	 */
	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.編譯


後記:
    分享完畢,歡迎大家拍磚和提問哦。共同進步……


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章