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