Android:支持https,否则报错:javax.net.ssl.SSLHandshakeException: java.security.cert.Cer

896 阅读3分钟

一、前言:

项目中使用的是okhttp,https请求报异常:

     Caused by: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
        at org.conscrypt.ConscryptFileDescriptorSocket.startHandshake(ConscryptFileDescriptorSocket.java:230)
        at okhttp3.internal.connection.RealConnection.connectTls(RealConnection.java:320)
        at okhttp3.internal.connection.RealConnection.establishProtocol(RealConnection.java:284)
        at okhttp3.internal.connection.RealConnection.connect(RealConnection.java:169)
        at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:258)
        at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
        at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
        at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
        at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:127)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
        at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)

是由于Https使用的证书的问题,Https是安全的http请求,请求时需要验证SSL证书。

#####上述报错的原因有几种情况:

  • 1.https服务器还没有配置SSL证书。
  • 2.https服务器配置的不是正规的CA机构签发的证书,可能是二级代理商等签发的证书,验证不通过造成的。
  • SSL/TLS证书是需要去专门的CA机构购买的,一般是买一年期的。当然CA机构也提供免费的CA证书,但是有效期一般比较短,比如只有三个月,且安全性不高。
  • SSL/TLS证书提供商的常见品牌:Symantec、Comodo、GeoTrust、GlobalSign、RapidSSL、DigiCert、GlobalSign等

延伸的思考:

-1、https请求时在握手阶段请求下来的ssl证书存储在哪里?还是说不缓存,只存储在内存中? -2、客户端验证证书过程?是将证书发给CA机构进行验证,还是根据证书里的CA机构的签名以及CA机构的公钥进行验证? -3、数字证书提供商靠谱吗? -4、没有域名只有ip可以使用https请求吗?

在CA机构申请CA证书时填写的是域名,所以,证书里的信息绑定的是域名,跟ip无关,因此ip+端口的形式无法使用https请求,因为证书不能绑定到ip,请求时无法获取到证书?初步理解,有待考证…

AWS也是个CA机构,证书费用每个月3千多美刀,一年3-4万美刀。 阿里云的ssl服务是以代理商的方式,本身不是CA机构,证书费用一年两千多。

二、解决:

1、解决方案1:忽略https的证书校验

package com.example.myapplication.utils;

import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

/**
 * @author lyy
 * 方案一:让okhttp支持https请求
 */
public class SSLSocketClient {

    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private static TrustManager[] getTrustManager() {
        return new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
    }

    public static X509TrustManager geX509tTrustManager() {
        return new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[]{};
            }
        };
    }

    public static HostnameVerifier getHostnameVerifier() {
        return new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };
    }
}

使用:

   private val retrofitBuilder by lazy {
        Retrofit.Builder()
            .baseUrl("https://www.baidu.com")
            .client(
                OkHttpClient.Builder()
                    //1、SSL配置一
                    .sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.geX509tTrustManager())
                    .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
                    //2、SSL配置一
//                  .sslSocketFactory(SSLSocketClient2.getSSLSocketFactory())//配置
//                  .hostnameVerifier(SSLSocketClient2.getHostnameVerifier())//配置
                        
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(5, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS).build()
            ).build()
    }

2、解决方案2:忽略https的证书校验

package com.example.myapplication.utils;

import java.security.SecureRandom;
import java.security.cert.X509Certificate;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
/**
 * @author lyy
 * 方案二:让okhttp支持https请求
 */
public class SSLSocketClient2 {
    //获取这个SSLSocketFactory
    public static SSLSocketFactory getSSLSocketFactory() {
        try {
            SSLContext sslContext = SSLContext.getInstance("SSL");
            sslContext.init(null, getTrustManager(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    //获取TrustManager
    private static TrustManager[] getTrustManager() {
        TrustManager[] trustAllCerts = new TrustManager[]{
                new X509TrustManager() {
                    @Override
                    public void checkClientTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public void checkServerTrusted(X509Certificate[] chain, String authType) {
                    }

                    @Override
                    public X509Certificate[] getAcceptedIssuers() {
                        return new X509Certificate[]{};
                    }
                }
        };
        return trustAllCerts;
    }

    //获取HostnameVerifier
    public static HostnameVerifier getHostnameVerifier() {
        HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                return true;
            }
        };
        return hostnameVerifier;
    }
}

使用:

 private val retrofitBuilder by lazy {
        Retrofit.Builder()
            .baseUrl("https://www.baidu.com")
            .client(
                OkHttpClient.Builder()
                    //1、SSL配置一
//                    .sslSocketFactory(SSLSocketClient.getSSLSocketFactory(), SSLSocketClient.geX509tTrustManager())
//                    .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
                    //2、SSL配置一
                  .sslSocketFactory(SSLSocketClient2.getSSLSocketFactory())//配置
                  .hostnameVerifier(SSLSocketClient2.getHostnameVerifier())//配置

                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(5, TimeUnit.SECONDS)
                    .writeTimeout(5, TimeUnit.SECONDS).build()
            ).build()
    }

详细解释查看:blog.csdn.net/yzpbright/a…