Android https忽略证书信任问题

发布时间 2023-03-29 12:16:56作者: cps666

【第一部分,忽略证书信任问题】 直接去第二部分性能问题
搬运自:https://blog.csdn.net/lizeyang/article/details/18983843
java程序在访问https资源时,出现报错

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

1)https通信过程

(1)客户使用https的URL访问Web服务器,要求与Web服务器建立SSL连接。

(2)Web服务器收到客户端请求后,会将网站的证书信息(证书中包含公钥)传送一份给客户端。

(3)客户端的浏览器与Web服务器开始协商SSL连接的安全等级,也就是信息加密的等级。

(4)客户端的浏览器根据双方同意的安全等级,建立会话密钥,然后利用网站的公钥将会话密钥加密,并传送给网站。

(5)Web服务器利用自己的私钥解密出会话密钥。

(6)Web服务器利用会话密钥加密与客户端之间的通信。

 
image

2)java程序的证书信任规则

如上文所述,客户端会从服务端拿到证书信息。调用端(客户端)会有一个证书信任列表,拿到证书信息后,会判断该证书是否可信任。

如果是用浏览器访问https资源,发现证书不可信任,一般会弹框告诉用户,对方的证书不可信任,是否继续之类。

Java虚拟机并不直接使用操作系统的keyring,而是有自己的security manager。与操作系统类似,jdk的security manager默认有一堆的根证书信任。如果你的https站点证书是花钱申请的,被这些根证书所信任,那使用java来访问此https站点会非常方便。因此,如果用java访问https资源,发现证书不可信任,则会报文章开头说到的错误。

解决问题的方法

1)将证书导入到jdk的信任证书中(理论上应该可行,未验证)

2)在客户端(调用端)添加逻辑,忽略证书信任问题

第一种方法,需要在每台运行该java程序的机器上,都做导入操作,不方便部署,因此,采用第二种方法。下面贴下该方法对应的代码。

1)先实现验证方法

void trustAllHttpsCertificates() throws Exception {
        TrustManager[] trustAllCerts = new TrustManager[1];
        trustAllCerts[0] = new MyTrustManager();
        SSLContext sslCtx = SSLContext.getInstance("SSL");
        sslCtx.init(null, trustAllCerts, null);
        javax.net.ssl.SSLSocketFactory sslsocketfactory = sslCtx.getSocketFactory();
        javax.net.ssl.HttpsURLConnection.setDefaultSSLSocketFactory(sslsocketfactory);
    }

class MyTrustManager implements TrustManager, X509TrustManager {
        public X509Certificate[] getAcceptedIssuers() {
            return null;
        }

        public boolean isServerTrusted(X509Certificate[] certs) {
            return true;
        }

        public boolean isClientTrusted(X509Certificate[] certs) {
            return true;
        }

        public void checkServerTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }

        public void checkClientTrusted(X509Certificate[] certs, String authType)
                throws CertificateException {
            return;
        }
    }

2)在访问https资源前,调用

try {
            trustAllHttpsCertificates();
        } catch (Exception e) {
            e.printStackTrace();
        } //setDefaultSSLSocketFactory
        HostnameVerifier hv = (urlHostName, session) -> {
            Log.e("apibhuc", "Warning: URL Host: v7" + urlHostName + " vs. " + session.getPeerHost());
            return true;
        };
        HttpsURLConnection.setDefaultHostnameVerifier(hv);  // outer try catch

【第二部分,以上代码性能问题】

如果你像上面说的每次请求都添加上面的代码,那么会有性能问题,具体就是访问耗时,应将将上面部分代码放至应用初始化时;