javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException

本文详细分析了HTTPS请求中遇到的SSLHandshakeException异常,指出问题可能源于服务器证书未配置或非正规CA签发。解释了SSL/TLS证书的验证流程,包括TrustManager用于验证证书本身的有效性,而HostnameVerifier则确认证书与请求的域名匹配。同时,介绍了如何自定义TrustManager和HostnameVerifier以忽略证书验证,以及OkHttp中相关方法的使用。最后,讨论了X509证书和信任管理器在HTTPS连接中的作用。

项目中使用的是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:

向后台获取最新的ca证书,从后台服务器下载或者存储在app本地都可。

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

有时候为了测试需要,可以忽略证书验证。

public class SSLSocketClient {
    //获取这个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;
    }
}

使用:

        String url = "https://wwww.baidu.com";
        OkHttpClient okHttpClient = new OkHttpClient()
                .newBuilder()
                .sslSocketFactory(SSLSocketClient.getSSLSocketFactory())//配置
                .hostnameVerifier(SSLSocketClient.getHostnameVerifier())//配置
                .build();
        final Request request = new Request.Builder()
                .url(url)
                .get()
                .build();
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {[添加链接描述](https://docs.oracle.com/javase/8/docs/api/javax/net/ssl/SSLSocketFactory.html)
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d(TAG, "onFailure: ");
            }
 
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d(TAG, "onResponse: " + response.body().string());
            }
        });

OkHttpClient.Builder的sslSocketFactory方法

该方法的官方介绍:

//OkHttpClient.java

	...
 
    /**
     * Sets the socket factory and trust manager used to secure HTTPS connections. If unset, the
     * system defaults will be used.
     *
     * <p>Most applications should not call this method, and instead use the system defaults. Those
     * classes include special optimizations that can be lost if the implementations are decorated.
     *
     * <p>If necessary, you can create and configure the defaults yourself with the following code:
     *
     * <pre>   {@code
     *
     *   TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
     *       TrustManagerFactory.getDefaultAlgorithm());
     *   trustManagerFactory.init((KeyStore) null);
     *   TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
     *   if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
     *     throw new IllegalStateException("Unexpected default trust managers:"
     *         + Arrays.toString(trustManagers));
     *   }
     *   X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
     *
     *   SSLContext sslContext = SSLContext.getInstance("TLS");
     *   sslContext.init(null, new TrustManager[] { trustManager }, null);
     *   SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
     *
     *   OkHttpClient client = new OkHttpClient.Builder()
     *       .sslSocketFactory(sslSocketFactory, trustManager)
     *       .build();
     * }</pre>
     */
    public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
   
   
      if (sslSocketFactory == null) throw new NullPointerException("sslSocketFactory == null");
      if (trustManager == null) throw new NullPointerException("trustManager == null");
      this.sslSocketFactory = sslSocketFactory;
      this.certificateChainCleaner = CertificateChainCleaner.get(trustManager);
      return this;
    }

	...

该方法有两个参数:sslSocketFactory和trustManager。

sslSocketFactory是用于创建SSLSocket的。

trustManager是用于传递给CertificateChainCleaner.get(trustManager);的,是用于构造证书链清洁器CertificateChainCleaner并用于清理证书列表的:

//CertificateChainCleaner.java


/**
 * Computes the effective certificate chain from the raw array returned by Java's built in TLS APIs.
 * Cleaning a chain returns a list of certificates where the first element is {@code chain[0]}, each
 * certificate is signed by the certificate that follows, and the last certificate is a trusted CA
 * certificate.
 *
 * <p>Use of the chain cleaner is necessary to omit unexpected certificates that aren't relevant to
 * the TLS handshake and to extract the trusted CA certificate for the benefit of certificate
 * pinning.
 */
public abstract class CertificateChainCleaner {
   
   
  public abstract List<Certificate> clean(List<Certificate><
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值