20190726 ApacheHttpClient-自签证书与系统证书共存

要使用HTTPClient-4.5.2做一些连接;但是有的域名是使用的自签证书,有的是CA签发证书;同时又不想跳过证书验证又想使用一个HTTPClient;

如果单单实现验证只使用自签证书或者只使用CA证书的域名比较简单;前者只需要new loadTrustMaterial(File file, char[] storePassword).

首先,不妨思考下2分钟:自签证书的12306网站 以前的12306网站使用的就是自签证书,它会提示你下载安装根证书以确保链接的安全,是为什么呢?

首先需要明白jks是什么,HTTPS连接建立过程。

1.jks就是java keyStore,keyStore是个啥东西。不同场景下意义是不同的;在HTTPClient指双向证书验证时需要这个(loadKeyStoreManager),含有公钥和私钥,keyStore一般是用密码保护的,私钥本身自己也是一个密码.

https://stackoverflow.com/questions/23202046/what-is-keystore

2.数字签名技术,CA使用自己的证书私钥加密一个普通企业和机构的一个公钥,这个过程就是签名,签名的结果就是这个企业域名(或软件)的数字证书,服务器把这个东西下发给浏览器,浏览器使用预置在电脑系统的根证书或者浏览器自己的证书库,但基本都是前面那些CA的根证书,用它们的公钥去验证发过来的数字证书是否CA为这个域名签发的,数字证书签发和验证这一过程用到非对称密钥加密;验证通过,浏览器安全地随机一些字符并使用发过来的证书的公钥进行加密,服务器收到后用它的私钥进行解密;然后服务器和浏览器之间就使用这个字符来加密它们之间的流量,这个是对称密钥加密;即加密和解密用一样的密钥或者可以相互计算出来。

大致的流程是这样,有些不太严谨,比如根证书验证数字签名的方法是加密服务器发过来的消息摘要和它自己带过来的签名是否一致,为何需要消息摘要呢(比如MD5)?因为RSA的同态性。

3.TLS是SSL的演进版本,SSL有很多Bug都是基于运输层的安全协议。https://www.differencebetween.com/difference-between-ssl-and-vs-tls/

知道了这些,基本可以明白,可以使用签发自签证书的根证书的公钥来验证服务器发送过来的自签证书了。

从keyStore中取出根证书,如果你不知道根证书的别名,可以遍历 keyStore.aliases()得出来。https://stackoverflow.com/questions/26711731/read-public-key-from-file-in-keystore

java 复制代码
 public static void init() throws Exception {
        rootCerStream = new FileInputStream(loadResourceFile(KEY_STORE_FILE_NAME));
        keyStore = KeyStore.getInstance("JKS");
        keyStore.load(rootCerStream,KEY_STORE_PASSWORD.toCharArray());
        rootCer =  keyStore.getCertificate(ROOT_CER_ALIAS);
    }

获取一个SSLContext来给HTTPClientBuilder来setSSLContext,因为需要SSLConnectionSocketFactory来创建SSL socket

java 复制代码
    public static SSLContext getMixSSLContext() throws Exception{
        SSLContext sslContext = SSLContextBuilder.create().loadTrustMaterial(null, new TrustStrategy() {
            @Override
            public boolean isTrusted(X509Certificate[] chain, String authType) throws CertificateException {
                for (X509Certificate cer : chain){
                    try {
                        cer.verify(rootCer.getPublicKey());
                    } catch (Exception e){
                        continue;
                    }
                    return true;
                }
                return false;
                //如果不想验证证书,直接return true就行了
            }
        }).build();
        return sslContext;
    }

同时有些时候,可能需要将 builder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE); 比如你直接访问了域名IP,或者你直接访问了ELB而不是那个签发域名,就会提示错误。当然最好的写法是使用new DefaultHostnameVerifier() ps:感觉还是SSLContextBuilder相当方便

java 复制代码
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.DefaultHostnameVerifier;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
....
  builder.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE);
  builder.setSSLHostnameVerifier(new DefaultHostnameVerifier());

当然还有一种解决方法就是将那根证书预置到java的CACert中

https://stackoverflow.com/questions/11143360/ssl-certificate-verification-in-java

了解些背景知识,还有源码,仔细阅读源码,也不是很难的。

相关推荐
周淳APP16 小时前
【HTTP之浏览器缓存及渲染】
前端·网络·网络协议·http·缓存
AI-小柒17 小时前
OpenClaw技术深度解析:从智能助手到自动化引擎的范式革命(附DataEyes实战)
大数据·运维·开发语言·人工智能·python·http·自动化
2601_9491465317 小时前
短信通知HTTP接口开发规范:基于RESTful/HTTP协议的短信API调用实现方案
网络协议·http·restful
Hello World . .19 小时前
Linux:网络编程-HTTP 协议
网络·网络协议·http
小杰帅气20 小时前
应用层的HTTP协议
网络·网络协议·http
周淳APP20 小时前
【HTTP1、HTTP2、HTTP3】
前端·网络·网络协议·http
未来之窗软件服务21 小时前
服务器运维(四十四)Python Gradio服务器伪请求pseudo http —东方仙盟
运维·服务器·http·仙盟创梦ide·东方仙盟
真夜21 小时前
又遇到生产与开发环境结果不一致问题。。。
前端·javascript·http
AI-小柒1 天前
巨省Token:OpenClaw安装部署并接入数眼智能特价模型全流程(包含Windows和Mac)
大数据·人工智能·windows·网络协议·tcp/ip·http·macos
历程里程碑1 天前
Linux 46 HTTPS(协议原理)安全通信全流程解析
linux·网络·c++·网络协议·http·https·排序算法