转载 : Android webview在https下实现ssl的双向认证

Posted 怀君

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了转载 : Android webview在https下实现ssl的双向认证相关的知识,希望对你有一定的参考价值。

解决APP应用安全报告WebView 无HTTPS 校验风险 的问题。

一、概述
1.简介
Https简单来说是Http的安全版,Https协议是由SSL+Http协议构建的可进行加密传输、身份认证的网络协议,比Http协议更加安全。

这里说的安全靠的就是SSL,SSL的作用如下:

a.认证用户和服务器,确保数据发送到正确的客户机和服务器。(验证证书)
b.加密数据防止传输数据中途被窃取。(加密)
c.维护数据的完整性,确保数据在传输过程中不被改变。(摘要算法)
Https在传输数据之前需要客户端与服务器端之间进行一次握手,只有握手通过了,才会有数据传输的过程。这个握手就是上面所说SSL作用的第一条,认证用户和服务器,这个认证的方式是用证书来实现的。

上面所述只是对Https做一个简单的介绍,更加全面具体的概述可以直接百度。

2.文章重点
这篇文章的重点是实现SSL证书双向认证,即客户端与服务器进行握手。

包含:

a.生成客户端与服务端证书。
b.搭建支持Https的服务器。
c.实现webview的双向证书认证。
二、生成证书
本篇文章用的是自签名证书,为什么不用权威机构颁发的证书?因为要钱!!!当然如果公司有去申请了最好。下面生成一个服务器端的自签名证书。

如何生成证书?在JDK中有个keytool工具,非常简单,只要安装了JDK都可以生成一个证书。直接命令行走起:

keytool -genkey -alias zx_server -keyalg RSA -keystore D:\\key\\zx_server.jks -validity 3600 -storepass 123456

使用上述的命令就可以在D盘的“key”文件夹生成一个服务器端的证书库文件 zx_server.jks,它的密钥库口令为:123456

接下来利用zx_server.jks证书库生成一个服务器端证书,它是可以被客户端拿来使用的:

keytool -export -alias zx_server -file D:\\key\\zx_server.cer -keystore D:\\key\\zx_server.jks -storepass 123456

生成了包含公钥的服务器端证书zx_server.cer。

三、下载tomcat,使用自签名证书配置Https
先去下载一个tomcat,本篇文章用的是tomcat 7,地址:http://tomcat.apache.org/download-70.cgi

压缩包接下后,找到tomcat/config/server.xml文件,以文本的形式打开。

在Servcie标签下,加入如下标签:

<Connector SSLEnabled="true" 
			acceptCount="100" 
			disableUploadTimeout="true" 
			enableLookups="true" 
			keystoreFile="D:/key/zx_server.jks" 
			keystorePass="123456" 
			maxSpareThreads="75" 
			maxThreads="200" 
			minSpareThreads="5" port="8443" 
			protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https" 
			secure="true" 
			sslProtocol="TLS"
			clientAuth="false" /> 

注意:keystoreFile的为刚刚生成zx_server.jks文件路径(这里填写自己的路径),keystorePass的值为密钥库口令:123456。

现在一个支持Https的tomcat服务器配好了,就是这么简单。找到tomcat/bin/startup.bat,直接双击启动服务器。

启动成功后,打开浏览器输入https://localhost:8443/ 即可看到证书不被信任的警告,无视它,直接进入,即可看见tomcat的默认主页。

四、webview实现双向认证
这步是本篇文章的重点。通过上述步骤一个https的服务器已经搭建好了,现在要实现双向认证,也就是说客户端也会有一个“jks文件”,服务器端要有一个“cer文件”与之对应,抽象来说就是客户端要去认证服务器端。

上面的步骤已经生成了zx_server.jks和zx_server.cer文件,按照生成上述证书的方式,生成两个客户端文件,命名为:zx_client.jks和zx_client.cer,现在去配置客户端证书。

1.配置服务器
服务器的配置非常简单,在上述第三步配置tomcat时添加的标签中,添加一些属性

<Connector SSLEnabled="true" 
			acceptCount="100" 
			disableUploadTimeout="true" 
			enableLookups="true" 
			keystoreFile="D:/key/zx_server.jks" 
			keystorePass="123456" 
			maxSpareThreads="75" 
			maxThreads="200" 
			minSpareThreads="5" port="8443" 
			protocol="org.apache.coyote.http11.Http11NioProtocol" scheme="https" 
			secure="true" 
			sslProtocol="TLS"
			clientAuth="true"
			truststoreFile="D:/key/zx_client.cer" /> 

上面的标签其它部分不变,只在标签的最后把clientAuth设置为true,同时多添加了一个属性truststoreFile,这个属性放的应该是我们刚刚生成的zx_client.cer文件,但加入后启动服务器会报错。现在我们把zx_client.cer这个文件添加在一个新生成的jks文件中:
keytool -import -alias D:\\key\\zx_client -file D:\\key\\zx_client.cer -keystore D:\\key\\zx_client_for_sever.jks

现在修改Server.xml文件,把truststoreFile属性的值改为刚刚生成的zx_client_for_sever.jks文件

<Connector //其它属性不变
			clientAuth="true"
			truststoreFile="D:/key/zx_client_for_sever.jks" /> 

这时重新启动tomcat,用浏览器再次访问刚刚的地址,会发现“ localhost 不接受您的登录证书,或者您的登录证书可能已过期。”

现在用浏览器已经不能访问我们的服务器了,接下来我们来配置android端,实现访问。

2.配置app
实现webview https的证书双向认证,说一下大概的实现思路:

a.代码中对证书做信任认证.
b.重写WebViewClient的shouldInterceptRequest方法,拦截WebView的Request请求,获取HttpsUrlConnection为其设置SSL的SocketFactory,利用这个HttpsUrlConnection拦截数据,然后返回新的WebResourceResponse给WebView。

按照上述思路开始之前先解决一个问题,Android平台只能识别bks格式的证书文件,现在我们就要把jks文件进行转换。

如何转换?方式很多,这里说一种,利用portecle,下载地址:https://sourceforge.net/projects/portecle/?source=typ_redirect

下载后解压文件,找到portecle.jar文件,直接双击即可运行,步骤基本上就是,选择要转换的jks文件->输入密码->选择转换的格式->保存文件,大体步骤如下:

a.选择jks文件

b.输入密码

c.选择转换的格式,会再次输入密码

d.提示转换成功,直接保存即可。

步骤很小白,还是贴出来了大体的步骤。

现在生产了一个zx_client.bks文件,再找到上面生产的zx_server.cer文件,将这两个文件放在Android assets文件夹下。

然后重写WebViewClient引入这两个证书,直接贴代码。

package com.zx.webview_ssl;
 
import android.annotation.TargetApi;
import android.net.Uri;
import android.os.Build;
import android.webkit.WebResourceRequest;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
 
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.KeyManagementException;
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 java.security.cert.X509Certificate;
 
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
 
public class SslWebViewClient extends WebViewClient 
 
    private SSLContext sslContext;
 
    public SslWebViewClient() 
        try 
            TrustManager[] trustManagers = prepareTrustManager(MyApplication.mContext.getResources().getAssets().open("zx_server.cer"));
            KeyManager[] keyManagers = prepareKeyManager(MyApplication.mContext.getResources().getAssets().open("zx_client.bks"), "123456");
            sslContext = SSLContext.getInstance("TLS");
            X509TrustManager trustManager = null;
            if (null != trustManagers)
                trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
            else 
                trustManager = new UnSafeTrustManager();
            
            sslContext.init(keyManagers, new TrustManager[]trustManager, new SecureRandom());
         catch (IOException e) 
            e.printStackTrace();
         catch (NoSuchAlgorithmException e) 
            e.printStackTrace();
         catch (KeyStoreException e) 
            e.printStackTrace();
         catch (KeyManagementException e) 
            e.printStackTrace();
        
    
 
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, String url) 
        return processRequest(Uri.parse(url));
    
 
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    @Override
    public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) 
        return processRequest(request.getUrl());
    
 
    private WebResourceResponse processRequest(Uri uri) 
        try 
            //设置连接
            URL url = new URL(uri.toString());
            HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();
            //为request设置SSL Socket Factory
            urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
 
            urlConnection.setHostnameVerifier(new HostnameVerifier() 
                @Override
                public boolean verify(String hostname, SSLSession session) 
                    return true;
                
            );
 
            //获取请求的内容、contentType、encoding
            InputStream inputStream = urlConnection.getInputStream();
            String contentType = urlConnection.getContentType();
            String encoding = urlConnection.getContentEncoding();
            if (null != contentType)
                String mimeType = contentType;
                if (contentType.contains(";"))
                    mimeType = contentType.split(";")[0].trim();
                
                //返回新的response
                return new WebResourceResponse(mimeType, encoding, inputStream);
            
 
         catch (MalformedURLException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
        return null;
    
 
    private TrustManager[] prepareTrustManager(InputStream... certificates) 
        if (certificates == null || certificates.length <= 0)
            return null;
        
        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)
 
                
            
            TrustManagerFactory trustManagerFactory = null;
            trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init(keyStore);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            return trustManagers;
         catch (NoSuchAlgorithmException e) 
            e.printStackTrace();
         catch (CertificateException e) 
            e.printStackTrace();
         catch (KeyStoreException e) 
            e.printStackTrace();
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
 
    
 
    private KeyManager[] prepareKeyManager(InputStream bksFile, String password) 
        try 
            if (bksFile == null || password == null)
                return null;
            
            KeyStore clientKeyStore = KeyStore.getInstance("BKS");
            clientKeyStore.load(bksFile, password.toCharArray());
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyManagerFactory.init(clientKeyStore, password.toCharArray());
            return keyManagerFactory.getKeyManagers();
         catch (KeyStoreException e) 
            e.printStackTrace();
         catch (NoSuchAlgorithmException e) 
            e.printStackTrace();
         catch (UnrecoverableKeyException e) 
            e.printStackTrace();
         catch (CertificateException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         catch (Exception e) 
            e.printStackTrace();
        
        return null;
    
 
    private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) 
        for (TrustManager trustManager : trustManagers) 
            if (trustManager instanceof X509TrustManager) 
                return (X509TrustManager) trustManager;
            
        
        return null;
    
 
    public static class MyTrustManager implements X509TrustManager
        private X509TrustManager defaultTrustManager;
        private X509TrustManager localTrustManager;
 
        public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException 
            TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            var4.init((KeyStore) null);
            defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
            this.localTrustManager = localTrustManager;
        
 
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException 
 
        
 
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException 
            try 
                defaultTrustManager.checkServerTrusted(chain, authType);
             catch (CertificateException ce) 
                localTrustManager.checkServerTrusted(chain, authType);
            
        
 
        @Override
        public X509Certificate[] getAcceptedIssuers() 
            return new X509Certificate[0];
        
    
 
    public static class UnSafeTrustManager implements X509TrustManager 
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException 
        
 
        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException 
        
 
        @Override
        public X509Certificate[] getAcceptedIssuersAndroid开发:关于WebView

Android开发:关于WebView

Android开发:关于WebView

Android开发:关于WebView

android webview

Android WebView基本使用