https中密码套件协商与证书校验

发布时间 2023-04-17 20:16:28作者: N!CE波

 

一、Ciphers(密码套件)

1)Ciphers(密码套件)协商过程

Ciphers(密码套件)协商过程,通常涉及到多个算法的比较和选择。

1、在TLS/SSL握手期间,客户端发送一个客户端支持的密码套件列表给服务端。该列表包含了客户端支持的密码套件。服务端在收到该列表后,会选择一种与客户端相同的密码套件,并将其返回给客户端。

2、如果服务端没有任何一种密码套件与客户端匹配,TLS握手将失败。

3、一旦客户端和服务端都同意使用相同的密码套件,它们将执行密钥交换,以创建一个共享密钥。该共享密钥用于对传输数据进行加密和解密。

 

2)密码套件的选择是通过以下步骤完成的

1、客户端发送支持的密码套件列表给服务端。

2、服务端选择自己支持的密码套件,并从客户端发送的列表中选择一个共同支持的密码套件。

3、服务端将共同支持的密码套件发送给客户端。

4、客户端选择共同支持的密码套件,并将其发送给服务端。

5、客户端和服务端使用共同支持的密码套件来加密和解密通信数据。

 

3)密码套件通常包含以下几个要素

1、对称加密算法:用于加密和解密数据的算法,例如AES、DES等。

2、密钥交换算法:用于在客户端和服务端之间交换密钥的算法,例如RSA、Diffie-Hellman等。

3、摘要算法:用于验证数据完整性的算法,例如MD5、SHA等。

4、协议版本:用于指定使用的安全协议版本,例如TLS 1.2、TLS 1.3等。

 

4) 使用Java打印OkHttpClient中支持的密码套件

        // 打印密码套件
        OkHttpClient okHttpClient = new OkHttpClient();;
        List<ConnectionSpec> connectionSpecs = okHttpClient.connectionSpecs();
        for (ConnectionSpec connectionSpec : connectionSpecs) {
            log.info("cipherSuites: {}", JsonUtil.toJson(Optional.ofNullable(connectionSpec.cipherSuites()).orElse(new ArrayList<>()).stream().map(CipherSuite::javaName).collect(Collectors.toList())));
        }

  

 

二、服务端证书校验

1)证书校验过程

在TLS握手期间,客户端会收到服务端的数字证书,客户端需要对数字证书进行校验,以确定它是否由受信任的证书颁发机构颁发,并且是否与服务端的域名匹配。

在Java中,客户端校验服务端证书的逻辑是由X509TrustManager接口实现的。X509TrustManager是Java中负责数字证书校验的核心接口,用于确定是否信任一个数字证书。

在X509TrustManager接口中,有一个方法checkServerTrusted(),用于校验服务端证书的合法性。checkServerTrusted()方法接收两个参数:一个是服务端的数字证书链,另一个是数字证书的算法名称。

 

客户端在校验服务端证书时,会遍历数字证书链中的所有证书,依次校验它们的合法性。校验的过程包括以下几个步骤:

1、首先,客户端会检查数字证书链中的最后一个证书是否是受信任的证书颁发机构颁发的证书。如果不是,则校验失败,TLS握手失败,HTTPS请求无法继续进行。

2、如果数字证书链中的最后一个证书是受信任的证书颁发机构颁发的证书,那么客户端会继续向上遍历数字证书链,依次校验每一个证书。每个证书都必须由下一个证书颁发机构签名,并且公钥可以用于验证下一个证书的签名。

3、客户端还会校验数字证书中的主体名称(Subject)和服务端的域名是否匹配。如果不匹配,则校验失败,TLS握手失败,HTTPS请求无法继续进行。

4、最后,如果数字证书校验通过,客户端将信任该数字证书,并使用该数字证书与服务端建立加密连接。

 

2)JRE中的信任库数据来源

JRE中的信任库数据来源于多个方面。以下是一些主要的来源:

1、JRE自带的根证书:JRE自带一些常见的根证书,如VeriSign、Thawte、GeoTrust等。

2、操作系统的根证书:JRE也会加载操作系统中安装的根证书(JRE安装时会复制一份到信任库)。

3、从JAR文件中读取证书:Java应用程序可以将证书文件打包在JAR文件中,然后使用Java代码加载这些证书(该操作不会复制到信任库)。

4、用户自定义的证书:用户可以使用keytool命令或者Java代码将自定义的证书添加到信任库中,这些证书可以是自签名的证书、企业内部颁发的证书等。

 

在JRE中,信任库的默认路径为$JAVA_HOME/lib/security/cacerts,其中$JAVA_HOME为JRE的安装路径。该信任库默认由Java SE Development Kit(JDK)或Java Runtime Environment(JRE)提供,并且包含了许多常见的根证书。如果需要使用自定义的信任库,可以使用javax.net.ssl.trustStore和javax.net.ssl.trustStorePassword属性指定信任库的路径和密码。

需要注意的是,JRE中的信任库只包含被广泛信任和接受的证书颁发机构(CA)签发的证书,而不包括自签名证书等不被广泛接受的证书。如果需要使用自签名证书等不被信任的证书,需要将其添加到信任库中或者在代码中进行特殊处理。

 

3)操作系统的根证书

复制操作系统中的根证书到JRE的信任库中,是在JRE安装时进行的。

当用户安装JRE时,安装程序会自动复制操作系统中的根证书到JRE的信任库中。这样一来,在JRE中的应用程序就可以使用这些根证书进行SSL连接的验证了。具体来说,安装程序会将操作系统中的根证书复制到以下目录:

Windows系统:%JAVA_HOME%\jre\lib\security\cacerts
Linux/Unix系统:$JAVA_HOME/jre/lib/security/cacerts
其中,%JAVA_HOME%或$JAVA_HOME表示JRE的安装目录。复制根证书时,安装程序还会使用预先定义好的密码来保护这些证书,这个密码是changeit。

当然,如果开发人员需要使用其他的根证书进行SSL连接的验证,可以使用keytool等工具,将这些根证书添加到JRE的信任库中。

 

4) 使用Java打印信任库中的证书

// 打印证书
        X509TrustManager x509TrustManager = Util.platformTrustManager();
        X509Certificate[] acceptedIssuers = x509TrustManager.getAcceptedIssuers();
        for (X509Certificate acceptedIssuer : acceptedIssuers) {
            log.info("IssuerDN: {}", acceptedIssuer.getIssuerDN());
        }