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

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

相关推荐
Karoku06611 小时前
【网站架构部署与优化】web服务与http协议
linux·运维·服务器·数据库·http·架构
友友马14 小时前
『 Linux 』HTTP(一)
linux·运维·服务器·网络·c++·tcp/ip·http
2401_8725149714 小时前
深入探究HTTP网络协议栈:互联网通信的基石
网络·网络协议·http
清水白石00814 小时前
C++使用Socket编程实现一个简单的HTTP服务器
服务器·c++·http
不良人天码星15 小时前
HTTP 协议的基本格式
网络·网络协议·http
活老鬼1 天前
Web与HTTP
网络·网络协议·http
天启代理ip1 天前
HTTP代理PAC脚本:自动化代理设置的利器
网络协议·http·自动化
小霸王_300378631 天前
《网络协议 - HTTP传输协议及状态码解析》
网络·网络协议·http
SchneeDuan1 天前
HTTP和HTTPS的区别
网络协议·http·https·ssl/tls协议·数字证书、签名
小堃学编程2 天前
计算机网络(六) —— http协议详解
网络协议·计算机网络·http