背景:
因为与境外银行做了API对接,采用了一些列的安全认证,认证细节不谈,其中接口链接都是用到htts(http+SSL),恰逢年末,证书都将到期,银行更换了SSL证书并邮件过来了客户端证书,现在就看我们怎么做了
方案其实无外乎两种,一是加载证书,二是忽略证书。
方案一:客户端加载证书
优点:安全性更高
缺点:服务器证书升级变动,需要同步操作
public CloseableHttpClient createSSLClient(){
try {
KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
//加载证书文件,最好制作根证书,否则每次证书签注,都得同步调整
FileInputStream instream = new FileInputStream(new File("D:/my/last.store"));
try {
trustStore.load(instream, "mypassword".toCharArray());
} finally {
instream.close();
}
SSLContext sslcontext = SSLContexts.custom()
//加载服务端提供的truststore(如果服务器提供truststore的话就不用忽略对服务器端证书的校验了)
.loadTrustMaterial(trustStore,null)
.build();
SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
sslcontext,
new String[]{"TLSv1.2"},
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier());
CloseableHttpClient httpclient = HttpClients.custom()
.setSSLSocketFactory(sslConnectionSocketFactory)
.build();
CloseableHttpClient closeableHttpClient = null;
//是否启用配置的代理
if (isProxy) {
HttpHost host = new HttpHost(proxyIP, proxyPort, scheme);
closeableHttpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).setProxy(host).build();
} else {
closeableHttpClient = HttpClients.custom().setSSLSocketFactory(sslConnectionSocketFactory).build();
}
return closeableHttpClient;
} catch (Exception e) {
logger.error("createSSLClientDefault exception",e);
}
return HttpClients.createDefault();
}
方案二:客户端忽略证书(当前采用的方式)
优点:忽略证书验证的方式,无论服务商怎么更新都没关系
缺点:暂未发现什么确定
public CloseableHttpClient createSSLClientDefault(){
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
//信任所有
public boolean isTrusted(X509Certificate[] chain,
String authType) throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext);
CloseableHttpClient closeableHttpClient = null;
//是否启用配置的代理
if(isProxy){
HttpHost host = new HttpHost(proxyIP, proxyPort,scheme);
closeableHttpClient = HttpClients.custom().setSSLSocketFactory(sslsf).setProxy(host).build();
}else{
closeableHttpClient = HttpClients.custom().setSSLSocketFactory(sslsf).build();
}
return closeableHttpClient;
} catch (Exception e) {
logger.error("createSSLClientDefault exception",e);
}
return HttpClients.createDefault();
}
还有一点比较重要,那就是 对证书的处理
无论是从浏览器上下载的crt、pem、cer,亦或是银行邮件提供过来的,多都是BASE64公钥,需要处理成JAVA可操作的类库,即JKS
完整的证书链,一般都包含3部分,根证书、中间证书、结尾证书(暂时如此命名),一般都是3段如下格式的文本
-----BEGIN CERTIFICATE-----
XXX……
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
XXX……
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
XXX……
-----END CERTIFICATE-----
处理证书需要借助JDK的keytool工具
#密码:mypassword
#根证书
keytool -import -alias "testcld-root-api" -file testcld/Entrust_Root_Certification_Authority_G2.crt -keystore testcld_root.store
#中间证书
keytool -import -alias "testcld-middle-api" -file testcld/Entrust_Certification_Authority_L1M.crt -keystore testcld_middle.store
#结尾证书
keytool -import -alias "testcld-last-api" -file testcld/testcld-enterprise-api.dbs.com -keystore last.store
- 我自己尝试了一下,按照方案一,上面3个类库都可以使用,但建议采用内根证书转换的类库,原因很简单,每次证书续签,变化的都是结尾证书,更证书不会变
- 对上面JAVA代码有疑惑的,可以尝试度娘
CloseableHttpClient
、SSLContextBuilder
、loadTrustMaterial
深入跟踪一下,相信会有收获
郑重感谢知乎大神的知识整理分享
HTTPS的原理及使用