背景
远程调用第三方服务时,之前都是双向认证,服务器提供jks格式的keystore证书,客户端配置好即可。
今天遇到个奇葩需求,服务器只给根公钥证书(root.crt),还是第三方合法证书,要求单向认证,客户端校验SSL握手时服务器发送的证书,只给了crt公钥。。。。真的服了。没办法,只能自己冲浪解决了,下面是针对我的这种情况,代码实践。测试没啥问题。以供搜到的你参考。
代码
java
//这个类实现证书校验
import javax.net.ssl.X509TrustManager;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
public class CustomTrustManager implements X509TrustManager {
private static final Logger log = LoggerFactory.getLogger(CustomTrustManager.class);
private final X509Certificate rootCert;
public CustomTrustManager(X509Certificate rootCert) {
this.rootCert = rootCert;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// 根据需求实现检查逻辑
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
boolean found = false;
final PublicKey publicKey = rootCert.getPublicKey();
for (X509Certificate cert : chain) {
try {
cert.verify(publicKey);
found = true;
break;
} catch (NoSuchAlgorithmException | SignatureException | InvalidKeyException |
NoSuchProviderException e) {
log.error("Failed to verify client certificate", e);
}
}
if (!found) {
throw new CertificateException("No trusted certificate found in the server's certificate chain.");
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
测试代码
java
@Test
void testCerts() throws NoSuchAlgorithmException, KeyManagementException, IOException {
//Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
X509Certificate certificate;
//加载根证书
try (InputStream inputStream = new FileInputStream("D:\\certs\\root-new.crt")) {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
certificate = (X509Certificate) certificateFactory.generateCertificate(inputStream);
} catch (IOException | java.security.cert.CertificateException e) {
throw new RuntimeException(e);
}
X509Certificate rootCert = certificate;
// 创建SSL上下文并设置为信任所有证书
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new CustomTrustManager(rootCert)}, null);
// 获取HttpsURLConnection实例
HttpsURLConnection connection = (HttpsURLConnection) new URL("https://你的URI").openConnection();
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.connect();
System.out.println("2222222222");
connection.disconnect();
System.out.println("11111111111");
}
完!