问题来源
用java开发爬虫程序时,我使用了webmagic框架,该框架可绕过部分https网站的证书,但对于某些尤其是银行类的网站,就遇到了这样的错误信息:
“javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates does not conform to algorithm constraints”。
于是在网上一阵搜,都说出现该异常的原因是在JDK中做了限制。
解决方案
1.修改jdk文件
找到该文件:$JAVA_HOME/jre/lib/security/java.security
把其中的两行配置项:
jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 768
改成:
jdk.certpath.disabledAlgorithms=,
jdk.tls.disabledAlgorithms=,
或者直接注释掉:
#jdk.certpath.disabledAlgorithms=MD2, MD5, RSA keySize < 1024
#jdk.tls.disabledAlgorithms=SSLv3, RC4, MD5withRSA, DH keySize < 768
我照着改了,没有效果,依然报“Certificates does not conform to algorithm constraints”这个错误。
不过,就算改了有效果,貌似也不能随便改jdk的文件吧。
所以又找了第二种方案。
2.继承抽象类X509ExtendedTrustManager
https://stackoverflow.com/questions/14149545/java-security-cert-certificateexception-certificates-does-not-conform-to-algori
上面提到了
Check if you have a custom trust manager implementing the older X509TrustManager
interface. JDK 7+ is supposed to be compatible with this interface, however based on my investigation when the trust manager implements X509TrustManager
rather than the newer X509ExtendedTrustManager
(docs), the JDK uses its own wrapper (AbstractTrustManagerWrapper
) and somehow bypasses the internal fix for this issue.
据说在jdk1.7这个问题其实已经修补了(我使用的是jdk1.8仍然有这个问题),
但是如果我们自定义一个TrustManager类,实现X509TrustManager这个接口,而不是继承 X509ExtendedTrustManager 这个抽象类,那么jdk底层会使用AbstractTrustManagerWrapper这个类来包裹实现X509TrustManager接口的类,当使用这个wrapper的时候,一定程度上有时候会绕过已经被修复的问题,导致Certificates does not conform to algorithm constraints会再度复现。
所以解决方案就算应该自定义的TrustManager类继承抽象类:X509ExtendedTrustManager,则SSLContextImpl就会使用我们自己的TrustManager来验证证书算法,而我们这个类所有的验证方法都是空方法(也就是不验证),那么自然也就不会抛异常了。
private SSLContext createIgnoreVerifySSL() throws NoSuchAlgorithmException, KeyManagementException {
// 继承X509ExtendedTrustManager抽象类,用于绕过验证,不用修改里面的方法
TrustManager[] trustAllCerts = new TrustManager[]{
new X509ExtendedTrustManager() {
@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {
}
@Override
public void checkClientTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string, Socket socket) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] xcs, String string, SSLEngine ssle) throws CertificateException {
}
}
};
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(null, trustAllCerts, new java.security.SecureRandom());
return sc;
}
如此修改后,确实没有再出现以上这个异常了。