Android开发WebView加载自签名认证的https网址

   之前webview都是显示http网址的,或者是显示出名的https网址,比如百度

    因为安全性问题,需要用上https防止别人抓取到请求链接,省钱就自签名生成cer和p12后缀的证书,这两个证书都是服务器端生成的,必须的(单向认证可以只有cer证书)。

    解决思路:webview中加入证书校验,如果是双向认证的,类似百度出来的忽略证书是行不通的。

    下面开始:重写WebViewClient
    

public class SslPinningWebViewClient extends WebViewClient {

    
    private SSLContext sslContext;

    public SslPinningWebViewClient()throws IOException {
        

        // 添加证书cer
        List certificates = new ArrayList<>();

        List certs_data = NetConfig.getCertificatesData();

        // 将字节数组转为数组输入流

        if (certs_data != null && !certs_data.isEmpty()) {

            for (byte[] bytes : certs_data) {

                certificates.add(new ByteArrayInputStream(bytes));

            }

        }

        prepareSslPinning(certificates);
    }

    @Override
    public WebResourceResponse shouldInterceptRequest (final WebView view, String url) {
        LogUtils.i("shouldInterceptRequest","shouldInterceptRequest1");
        return processRequest(url);

    }

    private WebResourceResponse processRequest(String webUrl) {
        LogUtils.i("SSL_PINNING_WEBVIEWS", "GET: " + webUrl.toString());

        try {
            // Setup connection
            URL url = new URL(webUrl);
            HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection();

            // Set SSL Socket Factory for this request
            urlConnection.setSSLSocketFactory(sslContext.getSocketFactory());
            urlConnection.setHostnameVerifier(new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return true;
                }
            });//很重要,校验证书

            // Get content, contentType and encoding
            InputStream is = urlConnection.getInputStream();
            String contentType = urlConnection.getContentType();
            String encoding = urlConnection.getContentEncoding();

            // If got a contentType header
            if(contentType != null) {

                String mimeType = contentType;

                // Parse mime type from contenttype string
                if (contentType.contains(";")) {
                    mimeType = contentType.split(";")[0].trim();
                }

                LogUtils.i("SSL_PINNING_WEBVIEWS", "Mime: " + mimeType);

                
                // Return the response
                return new WebResourceResponse(mimeType, encoding, is);
            }

        } catch (Exception e) {
            e.printStackTrace();
            
            LogUtils.i("SSL_PINNING_WEBVIEWS", e.getLocalizedMessage());
        }

        // Return empty response for this request
        return new WebResourceResponse(null, null, null);
    }

    private void prepareSslPinning(List certificates)throws IOException {

        try {

// 服务器端需要验证的客户端证书,其实就是客户端的keystore
            KeyManagerFactory keyManagerFactory = KeyManagerFactory
                    .getInstance("X509");

            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance("X509");

            KeyStore keyStore = KeyStore.getInstance("PKCS12");

            KeyStore keyStore2 = KeyStore.getInstance(KeyStore.getDefaultType());
            //读取证书
            InputStream ksIn = getResources().getAssets().open("***.p12");//***:你的p12证书

            //加载证书
            keyStore2.load(null);
            keyStore.load(ksIn, "****".toCharArray());//***:p12证书的密码,必须

            ksIn.close();
            CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
            try {
                for (int i = 0, size = certificates.size(); i < size; ) {
                    InputStream certificate = certificates.get(i);
                    String certificateAlias = Integer.toString(i++);
                    keyStore2.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
                    if (certificate != null)
                        certificate.close();
                }

            } catch (IOException e) {

                e.printStackTrace();

            }
            sslContext = SSLContext.getInstance("TLS");
//            TrustManagerFactory trustManagerFactory =
//
//                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());


            keyManagerFactory.init(keyStore, "yibanyiban".toCharArray());
            trustManagerFactory.init(keyStore2);
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);


        }catch (Exception e) {

            e.printStackTrace();

        }
    }
}

上面NetConfig读取cer证书

 

public class NetConfig {
    // 证书数据
    private static List CERTIFICATES_DATA = new ArrayList<>();
    /**
     * 添加https证书
     * @param inputStream
     */
    public synchronized static void addCertificate(InputStream inputStream) {

        if (inputStream != null) {
            try {
                int ava = 0;// 数据当次可读长度
                int len = 0;// 数据总长度
                ArrayList data = new ArrayList<>();
                while ((ava = inputStream.available()) > 0) {
                    byte[] buffer = new byte[ava];
                    inputStream.read(buffer);
                    data.add(buffer);
                    len += ava;
                }

                byte[] buff = new byte[len];
                int dstPos = 0;
                for (byte[] bytes:data) {
                    int length = bytes.length;
                    System.arraycopy(bytes, 0, buff, dstPos, length);
                    dstPos += length;
                }
                CERTIFICATES_DATA.add(buff);
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
    }

    /**
     * https证书
     * @return
     */
    public static List getCertificatesData() {
        return CERTIFICATES_DATA;
    }
}

读取cer证书,读取的位置看你项目的需求

 

/**
     * 读取cer证书
     */
    private void readCer(){
        // 添加https证书
        try {

            InputStream is = getAssets().open("tomcat.cer");
            NetConfig.addCertificate(is); // 这里将证书读取出来,,放在配置中byte[]里

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

以上就完成webview加载https网址了

 

花絮,花絮
如果你项目只需支持5.0系统版本以上的,webview有方法直接导入证书的,已测试过

wvMain.setWebViewClient(new WebViewClient() {

            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                view.loadUrl(url);
                return true;
            }

            

            @Override
            public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
                //super.onReceivedSslError(view, handler, error);

                LogUtils.i("mPrivateKey","SslError");
                handler.proceed();//接受证书

            }

            //加载https网页
            @Override
            public void onReceivedClientCertRequest(WebView view, ClientCertRequest request) {
                //super.onReceivedClientCertRequest(view, request);
                LogUtils.i("mPrivateKey","mPrivateKey");
                if ((null != HttpUtils.mPrivateKey)
                        && ((null != HttpUtils.mX509Certificates) && (HttpUtils.mX509Certificates.length != 0))) {

                    request.proceed(HttpUtils.mPrivateKey, HttpUtils.mX509Certificates);//这里的key,cer在你双向认证处理中都可以找到

                } else {
                    request.cancel();
                }
            }



        });

有问题可加QQ群:142739277


 

 

你可能感兴趣的:(android开发日记)