今天在对接一个银商的项目接口,测试环境调用,在写调用util的时候,有一些证书相关问题。
对于http换成支持https调用,网上有很多类的重写方法。就不细说了。
先把代码贴一下:
(还有2个类,我稍微改下晚点补上代码~)
SSLClientUtil.java
package com.naka.fizzbuzz;
import java.net.Socket;
import java.security.KeyStore;
import java.security.Security;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.*;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
public class SSLTrustClient extends SSLClient {
public SSLTrustClient() {
}
@Override
public void prepareCertificate() throws Exception {
// 跳过证书验证
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
SSLContext ctx = SSLContext.getInstance("TLS");
// 如果这里是,会报错:Algorithm constraints check failed on signature algorithm: MD5withRSA
X509TrustManager tm = new X509ExtendedTrustManager() {
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};
// 设置成已信任的证书
ctx.init(null, new TrustManager[] { tm }, null);
this.connectionSocketFactory = new SSLConnectionSocketFactory(ctx, new HostnameVerifier(){
@Override
public boolean verify(String hostname, SSLSession session) {
// 解决报错:javax.net.ssl.SSLPeerUnverifiedException: Certificate for doesn't match any of the subject alternative names: []
// 跳过域名验证
return true;
}
});
}
/**
* 因api需要TLSv1.1及以上版本,故新加一个方法实现
*/
@Override
public void prepareApiCertificate() throws Exception{
// 跳过证书验证
SSLContext ctx = SSLContext.getInstance("TLSv1.1");
X509TrustManager tm = new X509TrustManager(){
@Override
public void checkClientTrusted(X509Certificate[] chain,String authType) throws CertificateException{
}
@Override
public void checkServerTrusted(X509Certificate[] chain,String authType) throws CertificateException{
}
@Override
public X509Certificate[] getAcceptedIssuers(){
return null;
}
};
// 设置成已信任的证书
ctx.init(null, new TrustManager[] { tm }, null);
this.connectionSocketFactory = new SSLConnectionSocketFactory(ctx);
}
}
说一下遇到的2个问题:
1.跳过域名校验:
本来的代码是这样写的:
// 设置成已信任的证书
ctx.init(null, new TrustManager[] { tm }, null);
this.connectionSocketFactory = new SSLConnectionSocketFactory(ctx);
报错:
javax.net.ssl.SSLPeerUnverifiedException: Certificate fordoesn't match any of the subject alternative names: []
因为我在调用的时候是用ip调用的,默认会去校验域名
因此重写HostnameVerifier,总是return true,跳过校验
2.跳过证书算法校验:
报错:
Caused by: java.security.cert.CertPathValidatorException: Algorithm constraints check failed on signature algorithm: MD5withRSA
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: Certificates do not conform to algorithm constraints
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1964)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:328)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:322)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1614)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:216)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1052)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:987)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1072)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1385)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1413)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1397)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:436)
at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:384)
at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:142)
at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:374)
at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:393)
at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236)
at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:186)
at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:89)
at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110)
at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:185)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:83)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)
at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:56)
at com.naka.fizzbuzz.SSLClientUtil.doPost(SSLClientUtil.java:37)
at com.naka.fizzbuzz.SSLClientUtil.doPost(SSLClientUtil.java:28)
at com.naka.fizzbuzz.GiftcardTest.sendPostHttps(GiftcardTest.java:103)
at com.naka.fizzbuzz.GiftcardTest.demo2(GiftcardTest.java:63)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.security.cert.CertificateException: Certificates do not conform to algorithm constraints
at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(SSLContextImpl.java:1127)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAdditionalTrust(SSLContextImpl.java:1051)
at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:993)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1596)
... 52 more
Caused by: java.security.cert.CertPathValidatorException: Algorithm constraints check failed on signature algorithm: MD5withRSA
at sun.security.provider.certpath.AlgorithmChecker.check(AlgorithmChecker.java:278)
at sun.security.ssl.AbstractTrustManagerWrapper.checkAlgorithmConstraints(SSLContextImpl.java:1123)
... 55 more
这个报错,是校验证书不通过。而且这个是JDK1.8才会出现的问题,如果换回JDK1.7是不会报错的。
网上普遍的解决办法:
1. 在cmd窗口输入命令"where java"检查你实际使用的JDK到底在哪里(我用的是Windows7)
2. 重装那个JDK(这一步可能不需要)
3. 编辑文件java.security(C:\Program Files\Java\jdk1.8.0_191\jre\lib\security\java.security)
4. 去掉"jdk.certpath.disabledAlgorithms"里的"MD5",去掉"jdk.tls.disabledAlgorithms"里的"MD5withRSA"
5.打开环境变量设置,去掉path里的"C:\Program Files (x86)\Common Files\Oracle\Java\javapath;"
6.重启电脑
其实这个解决的就是JDK1.7和1.8配置文件的差别。
ok,本地环境这样改还可以,线上不能因为一个https调用改环境的jdk版本。
参考了一个博主的代码:https://blog.csdn.net/weixin_42330402/article/details/80510020
如果TrustAnyTrustManager是实现接口X509TrustManager,而不是继承抽象类:X509ExtendedTrustManager,那么JDK则会默认用自己的实现类AbstractTrustManagerWrapper来对服务器的证书算法进行验证,检查她们是否符合java.security里的配置要求!而如果我们的TrustAnyTrustManager继承了抽象类:X509ExtendedTrustManager,则SSLContextImpl就会使用我们自己的TrustAnyTrustManager来验证证书算法,而我们这个类所有的验证方法都是空方法(也就是不验证),那么自然也就不会抛异常了。
本来的代码是这样写的:
X509TrustManager tm = new X509TrustManager() {
@Override
public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException {
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
};