
我要实现的目标基本上可以归结为一个问题:
不必从文件加载信任库,而必须基于安全配置存储中的数据在内存中创建信任库。
事实证明这有些棘手,但绝对有可能。
创建信任库很容易:
KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType());ts.load(null);for (Certificate cert : certList) { ts.setCertificateEntry(UUID.randomUUID().toString(), cert);}但是,将其提供给SSL处理管道有点棘手。基本上,我们需要做的是提供一个
X509ExtendedTrustManager使用上面创建的信任库的实现。
为了让SSL处理管道知道此实现,我们需要实现自己的提供程序:
public class ReloadableTrustManagerProvider extends Provider { public ReloadableTrustManagerProvider() { super("ReloadableTrustManager", 1, "Provider to load client certificates from memory"); put("TrustManagerFactory." + TrustManagerFactory.getDefaultAlgorithm(), ReloadableTrustManagerFactory.class.getName()); }}该提供程序又使用以下
TrustManagerFactorySpi实现:
public class ReloadableTrustManagerFactory extends TrustManagerFactorySpi { private final TrustManagerFactory originalTrustManagerFactory; public ReloadableTrustManagerFactory() throws NoSuchAlgorithmException { ProviderList originalProviders = ProviderList.newList( Arrays.stream(Security.getProviders()).filter(p -> p.getClass() != ReloadableTrustManagerProvider.class) .toArray(Provider[]::new)); Provider.Service service = originalProviders.getService("TrustManagerFactory", TrustManagerFactory.getDefaultAlgorithm()); originalTrustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm(), service.getProvider()); } @Override protected void engineInit(KeyStore keyStore) throws KeyStoreException { } @Override protected void engineInit(ManagerFactoryParameters managerFactoryParameters) throws InvalidAlgorithmParameterException { } @Override protected TrustManager[] engineGetTrustManagers() { try { return new TrustManager[]{new ReloadableX509TrustManager(originalTrustManagerFactory)}; } catch (Exception e) { return new TrustManager[0]; } }}有关更多
originalTrustManagerFactory和
ReloadableX509TrustManager以后的内容。
最后,我们需要以一种使提供程序成为默认提供程序的方式注册提供程序,以便SSL管道可以使用它:
Security.insertProviderAt(new ReloadableTrustManagerProvider(), 1);
此代码可以在
main之前执行
SpringApplication.run。
回顾一下:我们需要将我们的提供程序插入安全提供程序列表中。我们的提供者使用我们自己的信任管理器工厂来创建我们自己的信任管理器的实例。
仍然缺少两件事:
- 实施我们的信托经理
- 的解释
originalTrustManagerFactory
首先,实现(基于https://donneyfan.com/blog/dynamic-java-truststore-for-a-jax-ws-
client):
public class ReloadableX509TrustManager extends X509ExtendedTrustManager implements X509TrustManager { private final TrustManagerFactory originalTrustManagerFactory; private X509ExtendedTrustManager clientCertsTrustManager; private X509ExtendedTrustManager serverCertsTrustManager; private ArrayList<Certificate> certList; private static Log logger = LogFactory.getLog(ReloadableX509TrustManager.class); public ReloadableX509TrustManager(TrustManagerFactory originalTrustManagerFactory) throws Exception { try { this.originalTrustManagerFactory = originalTrustManagerFactory; certList = new ArrayList<>(); reloadTrustManager(); } catch (Exception e) { logger.fatal(e); throw e; } } public void removeCertificate(Certificate cert) throws Exception { certList.remove(cert); reloadTrustManager(); } public void addCertificates(List<Certificate> certs) throws Exception { certList.addAll(certs); reloadTrustManager(); } @Override public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { clientCertsTrustManager.checkClientTrusted(chain, authType); } @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { clientCertsTrustManager.checkClientTrusted(x509Certificates, s, socket); } @Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { clientCertsTrustManager.checkClientTrusted(x509Certificates, s, sslEngine); } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { serverCertsTrustManager.checkServerTrusted(chain, authType); } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, Socket socket) throws CertificateException { serverCertsTrustManager.checkServerTrusted(x509Certificates, s, socket); } @Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s, SSLEngine sslEngine) throws CertificateException { serverCertsTrustManager.checkServerTrusted(x509Certificates, s, sslEngine); } @Override public X509Certificate[] getAcceptedIssuers() { return ArrayUtils.addAll(serverCertsTrustManager.getAcceptedIssuers(), clientCertsTrustManager.getAcceptedIssuers()); } private void reloadTrustManager() throws Exception { KeyStore ts = KeyStore.getInstance(KeyStore.getDefaultType()); ts.load(null); for (Certificate cert : certList) { ts.setCertificateEntry(UUID.randomUUID().toString(), cert); } clientCertsTrustManager = getTrustManager(ts); serverCertsTrustManager = getTrustManager(null); } private X509ExtendedTrustManager getTrustManager(KeyStore ts) throws NoSuchAlgorithmException, KeyStoreException { originalTrustManagerFactory.init(ts); TrustManager tms[] = originalTrustManagerFactory.getTrustManagers(); for (int i = 0; i < tms.length; i++) { if (tms[i] instanceof X509ExtendedTrustManager) { return (X509ExtendedTrustManager) tms[i]; } } throw new NoSuchAlgorithmException("No X509TrustManager in TrustManagerFactory"); }}此实现有一些值得注意的要点:
- 实际上,它将所有工作委托给普通的默认信任管理器。为了获得它,我们需要具有SSL管道通常使用的默认信任管理器工厂。这就是为什么我们将其作为
originalTrustManagerFactory
构造函数中的参数传递。 - 实际上,我们使用两种不同的信任管理器实例:一种用于验证客户端证书-当客户端向我们发送请求并使用客户端证书对其自身进行身份验证时使用-另一种用于验证服务器证书-当我们发送证书时使用使用HTTPS向服务器发送请求。为了验证客户端证书,我们使用自己的信任库创建了一个信任管理器。这将仅包含存储在我们的安全配置存储中的证书,因此将不包含Java通常信任的任何根CA。如果我们将这个信任管理器用于对我们作为客户端的HTTPS URL的请求,则该请求将失败,因为我们将无法验证服务器证书的有效性。因此,
getAcceptedIssuers
需要从我们的两个信任管理器中退回接受的颁发者,因为在这种方法中,我们不知道客户端或服务器证书是否正在进行证书验证。这具有一个小的缺点,即我们的信任管理器还将信任使用我们的自签名客户端证书作为HTTPS的服务器。
为了完成所有这些工作,我们需要启用ssl客户端身份验证:
server.ssl.key-store: classpath:keyStore.p12 # secures our API with SSL. Needed, to enable client certificates handlingserver.ssl.key-store-password: very-secureserver.ssl.client-auth: need
因为我们正在创建自己的信任库,所以不需要此设置
server.ssl.trust-store及其相关设置
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)