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

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

相关推荐
xnkyn18 小时前
frp内网穿透https访问本地服务,frpee客户端https教程
前端·后端·网络协议·http·https
IP老炮不瞎唠2 天前
SOCKS5 vs HTTP:哪种代理协议更适合你的业务场景
网络·网络协议·http
中科三方2 天前
HTTP劫持与DNS劫持有什么区别?如何识别和防范?
网络·网络协议·http·dns
小江的记录本2 天前
【Docker】 Docker 全平台部署(Linux / Windows / MacOS)与 前后端分离项目 容器化方案
java·linux·windows·http·macos·docker·容器
txinyu的博客2 天前
muduo http优化 —— 在原本数据监测http上 多支持了功能完善的http_1
网络·网络协议·http
小贺儿开发2 天前
Unity3D LED点阵屏幕模拟
http·unity·浏览器·网络通信·led·互动·点阵屏
小江的记录本2 天前
【Docker】《 Docker 高频常用命令速查表 》
java·前端·后端·http·docker·容器·eureka
Brookty2 天前
HTTP应用数据组织、HTML/CSS/JS数据描述及应用层数据处理介绍
css·网络·http·html
HwJack203 天前
HarmonyOS HTTP请求:从“能跑就行”到“优雅可靠”的进化之路
http·华为·harmonyos
AnalogElectronic3 天前
对https一系列问题的疑问与解答
网络协议·http·https