前言:     

        因为我们项目是Android + H5,之前的做法是把H5所需要资源html下载本地这样证书校验不会走系统浏览器层只需要项目中预埋根证书可以了,但是如果webview加载线上域名签名证书就会走系统校验onReceivedSslError返回ssl证书不受信,从而导致出现白页的情况

篇文章贵在直接给提供一个工具类按照步骤直接使用

使用方法

1、在自己appgradle添加okhttp依赖项目中如有可以忽略 

    implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    implementation 'com.squareup.okio:okio:1.14.0'
    implementation 'com.squareup.okhttp3:okhttp-urlconnection:3.11.0'

2、把服务器端给你的CA根证书(.cer后缀的,貌似.pem的不行但是自己改一下后缀好像可以用)放到自己assets目录

3、在自己webviewsetWebViewClient时候重写一下onReceivedSslError方法,在该方法处理ssl握手失败场景可以通过打印error.getPrimaryError()去SslError类中对比一下报错原因

mWebview.setWebViewClient(new WebViewClient() {
    ...
    @Override
			public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//				super.onReceivedSslError(view, handler, error);
				//自签名证书校验失败
				new CheckoutServerCaUtil().checkoutServerCA(handler,view.getUrl(),view.getContext(),"chain.cer");
			}
}

4、使用提供的工具直接校验工具如下创建名为CheckoutServerCaUtil的类直接ctrl + v 粘贴调用checkoutServerCA方法参数参考工具中的注释部分就可完成证书的校验主机名校验

package com.citicbank.cbframeworkcore.util;


import android.annotation.SuppressLint;
import android.content.Context;
import android.util.Log;
import android.webkit.SslErrorHandler;


import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.Arrays;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import javax.security.auth.x500.X500Principal;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

/**
 * author : jsxin
 * e-mail : jsxin0816@163.com
 * time   : 2022/11/17
 * desc   : 分行自签名证书校验工具:
 *          功能:1、根证书校验 2、主机名校验
 *          用法自己webview.setWebViewClient(new WebViewClient() {
 *              new CheckoutServerCaUtil().checkoutServerCA(handler,view.getUrl(),view.getContext());
 *          }
 */
public class CheckoutServerCaUtil {
    private final String TAG = "CheckoutServerCaUtil";
    public CheckoutServerCaUtil() {
    }

    /**
     * 根证书校验:onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
     * @param handler :   onReceivedSslError回调:SslErrorHandler
     * @param url :       onReceivedSslError回调:WebView.getUrl
     * @param context:    onReceivedSslError回调:WebView.getContext
     * @param caName:    放在assets目录下的根证书名称例如:"chain.cer"
     */
    public void checkoutServerCA(final SslErrorHandler handler, String url, Context context,String caName) {
        OkHttpClient.Builder builder;
        try {
            InputStream inputStream = context.getAssets().open(caName);
            Log.d(TAG,"jsxin--->>>执行:--->>>00--->>>url:" + url);
            builder = setCertificates(new OkHttpClient.Builder(), inputStream);

        } catch (IOException e) {
            Log.d(TAG,"jsxin--->>>异常:--->>>01");
            builder = new OkHttpClient.Builder();
        }
        Request request = new Request.Builder().url(url)
                .build();
        builder.build().newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException error) {
                Log.d(TAG,"jsxin--->>>自签名证书校验结果:---error:----" + error.toString());
                handler.cancel();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG,"jsxin--->>>自签名证书校验结果:---onResponse:----" + response.code());
                handler.proceed();
            }
        });
    }

    private OkHttpClient.Builder setCertificates(OkHttpClient.Builder client, InputStream... certificates) {

        try {
            Log.d(TAG,"jsxin--->>>执行:--->>>01");

            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) {
                    Log.d(TAG,"jsxin--->>>异常:--->>>02");
                    e.printStackTrace();
                }

            }
            Log.d(TAG,"jsxin--->>>执行:--->>>02");
            SSLContext sslContext = SSLContext.getInstance("TLS");

            TrustManagerFactory trustManagerFactory =

                    TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

            trustManagerFactory.init(keyStore);

            sslContext.init(null, trustManagerFactory.getTrustManagers(), new SecureRandom());

            SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                Log.d(TAG,"jsxin--->>>异常:--->>>03");
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }
            X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
            Log.d(TAG,"jsxin--->>>执行:--->>>03");

            client.sslSocketFactory(sslSocketFactory, trustManager);
            hostNameVerifier(client);
        } catch (Exception e) {
            Log.d(TAG,"jsxin--->>>异常:--->>>04");
            e.printStackTrace();

        }

        return client;

    }

    private void hostNameVerifier(OkHttpClient.Builder client) {
        Log.d(TAG,"jsxin--->>>执行:--->>>04");
        client.hostnameVerifier(new HostnameVerifier() {
            @SuppressLint("BadHostnameVerifier")
            @Override
            public boolean verify(String hostname, SSLSession session) {
                String peerHost = session.getPeerHost();//服务器返回域名
                Log.d(TAG,"jsxin--->>>执行:--->>>05--->>>服务器返回域名 peerHost:" + peerHost + "--->>>主机名:" + hostname);
                try {
                    X509Certificate[] peerCertificates = (X509Certificate[]) session.getPeerCertificates();
                    for (X509Certificate c : peerCertificates) {
                        X500Principal subjectX500Principal = c.getSubjectX500Principal();
//                        String name = new X500Principal(subjectX500Principal).getName();
                        String name = subjectX500Principal.getName();
                        Log.d(TAG,"jsxin--->>>执行:--->>>06--->>>subjectX500Principal.getName():" + name);
                        String[] split = name.split(",");
                        for (String s : split) {
                            if (s.startsWith("CN")) {
                                if (s.contains(hostname) && s.contains(peerHost)) {
                                    Log.d(TAG,"jsxin--->>>执行:--->>>07");
                                    return true;
                                }
                            }
                        }
                    }
                } catch (SSLPeerUnverifiedException e) {
                    Log.d(TAG,"jsxin--->>>异常:--->>>05");
                    e.printStackTrace();
                }
                Log.d(TAG,"jsxin--->>>主机名校验失败");
                return false;
            }
        });
    }
}

如果想系统学习一下可以参考这个链接网上基本上都是抄的这个但是里面有的一些方法过时了,所以可以直接梭哈我的工具Android Webview SSL 自签名安全校验解决方案 – 熠然 – 博客园

原文地址:https://blog.csdn.net/jsxin0816/article/details/127929069

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_37180.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注