问题描述

在访问某网站时,服务器报出了Algorithm constraints check failed on signature algorithm: MD2withRSA

降低java安全级别或添加网址到例外网站列表_服务器

这是因为在进行SSL握手时,服务器所采用的ssl证书算法不符合某种约束条件而抛出异常!

 

问题分析:

1. 查询网站证书信息

openssl s_client -showcerts -connect hostname:443

降低java安全级别或添加网址到例外网站列表_java_02

VeriSign(威瑞信)公司根证书的签名算法是md2WithRSAEncryption!

 

 2. 为什么使用md2WithRSAEncryption会出异常?

由于MD2被广泛认为是不安全的,JDK6u17版本开始,MD2算法被禁用。打开$JAVA_HOME/jre/lib/security/java.security文件可以看到

降低java安全级别或添加网址到例外网站列表_java_03

可以肯定,必定是代码中对算法做了校验引起的。

 

3. JDK代码实现原理

代码中我们在初始化SSLContext的时候调用了loadTrustMaterial方法

降低java安全级别或添加网址到例外网站列表_服务器_04

进入loadTrustMaterial方法,此时tm是X509ExtendedTrustManager的子类,进入SSLContextBuilder类

降低java安全级别或添加网址到例外网站列表_系统安全_05

trustManager被强转成父类X509TrustManager,最终被封装成了TrustManagerDelegate,TrustManagerDelegate实现了X509TrustManager接口。

降低java安全级别或添加网址到例外网站列表_java_06

 继续跟踪到sun.security.ssl.SSLContextImpl

降低java安全级别或添加网址到例外网站列表_JAVA_07

其中var1[var2]即TrustManagerDelegat对象,跳过判断,TrustManagerDelegate又被AbstractTrustManagerWrapper封装返回。

降低java安全级别或添加网址到例外网站列表_系统安全_08

AbstractTrustManagerWrapper继承了X509ExtendedTrustManager实现了X509TrustManager接口

降低java安全级别或添加网址到例外网站列表_JAVA_09

AbstractTrustManagerWrapper会对服务器的证书算法进行验证,检查它们是否符合java.security里的配置要求。

 

4. JDK修复方案 

JDK1.7之后修改了SSLContext的实现,解决了上述的问题 

降低java安全级别或添加网址到例外网站列表_系统安全_10

降低java安全级别或添加网址到例外网站列表_系统安全_11

定位sun.security.ssl.SSLContextImpl,默认var2为null,由var4.getTrustMangers初始化

降低java安全级别或添加网址到例外网站列表_JAVA_12

var2是一个TrustManager对象的数组,内部元素是X509TrustManagerImpl实例。

降低java安全级别或添加网址到例外网站列表_java_13

X509TrustManagerImpl继承了X509ExtendedTrustManager

降低java安全级别或添加网址到例外网站列表_JAVA_14

进入chooseTrustManager方法,最终返回X509TrustManagerImpl实例对象

降低java安全级别或添加网址到例外网站列表_JAVA_15

X509TrustManagerImpl类没有对服务端做证书算法校验的方法,因此如果用法不对,还是会导致Certificates does not conform to algorithm constraints复现。

 

解决方案 

方案一:

修改文件$JAVA_HOME/jre/lib/security/java.security,找到如下配置:

降低java安全级别或添加网址到例外网站列表_服务器_16

降低java安全级别或添加网址到例外网站列表_服务器_17

修改配置(或者直接注释)
jdk.certpath.disabledAlgorithms=
jdk.tls.disabledAlgorithms=

缺点:

1. 系统安全级别降低,JDK8提高了对SSL证书的算法安全要求
2. 线上所有服务器的JDK配置需要修改,如果增加新服务器也需要配置,维护麻烦

 

方案二:

自定义实现X509ExtendedTrustManager,JDK会使用TrustAnyTrustManager来验证证书算法,而这个类所有的验证方法都是空方法,也就是不验证。

降低java安全级别或添加网址到例外网站列表_系统安全_18

降低java安全级别或添加网址到例外网站列表_系统安全_19

缺点:

对服务端的证书无条件的信任是不安全的。

 

方案三:

我们选择JDK默认的实现方式

降低java安全级别或添加网址到例外网站列表_系统安全_10

 发现另外一个网站出现了PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target错误

降低java安全级别或添加网址到例外网站列表_系统安全_21

这个错误的原因是因为服务端的证书机构不被客户端信任

1. 通过openssl s_client -showcerts -connect hostname:443查询到CA机构是Symantec(赛门铁克)

2. 查询JDK默认信任的机构,测试类启动的时候加上命令-Djavax.net.debug=ssl,handshake,具体文件见$JAVA_HOME/jre/lib/security/cacerts

降低java安全级别或添加网址到例外网站列表_系统安全_22

经查询之后发现Symantec不是JDK信任的机构。将Symantec添加到JDK的cacerts中即可解决问题。