前段时间,遇到了一个微信支付调用Rest接口需要SSL授信的问题,遂看了一段时间自SSL证书的集成方式。

Java对安全证书的支持

JDK在java.security下有一个KeyStore的工具类,可以通过工厂方法getInstance(String type, String provider)初始化一个密钥存储对象,该对象对主流的密钥格式,如PKCS12、PEM都有了默认支持。

以下是初始化密钥的方法描述。

String KEYSTORE_TYPE = "PKCS12";
try (FileInputStream fis = new FileInputStream(certPath)) {
// 实例化密钥库
KeyStore ks = KeyStore.getInstance(KEYSTORE_TYPE);
// 加载密钥库
ks.load(fis, certPass.toCharArray());
// 实例化密钥库
KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
// 初始化密钥工厂
kmf.init(ks, certPass.toCharArray());
}catch (Exception e) {
log.error(e.getMessage(), e);
}

SSLSocketFactory

有了密钥,在应用上加解密的配置便完成了,可以使用密钥工厂对SSLSocket进行适配,产生安全连接。使用SSLContext可以初始化一个SSLSocketFactory。

String PROTOCOL_TYPE = "SSL";
SSLContext sslContext = SSLContext.getInstance(PROTOCOL_TYPE, "SunJSSE");
sslContext.init(kmf.getKeyManagers(), null, new SecureRandom());
// 获取SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();

至此,整个SSL连接已经构建完成。

OkHttp使用SSLSocketFactory

OkHttpClient.Builder()提供了一个public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager)方法可以给OKhttp配置一个SSL连接工厂,所有通过该Client的Request都会附带认证信息,其中X509TrustManager为X509密钥标准的抽象管理类。

OKhttp的文档中提供了X509TrustManager的初始化方法。

TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init((KeyStore) null);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
throw new IllegalStateException("Unexpected default trust managers:"
+ Arrays.toString(trustManagers));
}
X509TrustManager trustManager = (X509TrustManager) trustManagers[0];

最后通过OkHttpClient okHttpClient = new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory,trustManager).build()
便可以完成整个Client的初始化工作。

后记

作为后起之秀,OkHttp对于Java原有的证书授信机制有着深入的理解,并提供了使用SSLSocketFactory的适配方法,整个API设计简洁自然,杜绝了重复的设计。当然,没有所谓的绝对安全,对于重要的证书密钥应该妥善保管,并且及时更新,避免泄露带来的连锁反应。