У меня есть клиентское приложение SOAP, которое вызывает стороннюю конечную точку SOAP, и мы используем SSL для доступа к ней. Сейчас срок действия сертификата истекает через несколько месяцев, и мы получили новый сертификат нашей третьей стороны. Теперь мы используем клиент Apache CXF для подключения к конечной точке. Мы используем закрытый ключ и сертификат для создания ключевой записи в собственном хранилище ключей в коде Java.
Обратите внимание, что сторонняя сторона на стороне сервера еще не использовала новый сертификат и ожидает, что клиент поддержит оба одновременно, чтобы они могли легко выполнить миграцию.
Теперь я адаптировал наш код, чтобы добавить новый сертификат к этой существующей записи ключа. Потому что в коде в Keystore вы можете иметь один псевдоним и несколько сертификатов (цепочку сертификатов), определенных для записи ключа. Единственное, что клиент всегда использует первый сертификат при отправке SOAP-запроса.
Из моего понимания этой ссылки
Менеджер ключей по умолчанию всегда выбирает первый в цепочке сертификатов
Наша реализация Keystore:
@Bean
public Merlin merlin(KeyStore keyStore) throws WSSecurityException, IOException {
return new KeyStoreMerlin(keyStore);
}
@Bean
public KeyStore keyStore(SSLConnectionProperties sslConnectionProperties,
RandomPasswordCallbackHandler randomPasswordCallbackHandler) throws Exception {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate[] x509Certificates = sslConnectionProperties.getCertifcates().stream()
.map(crt -> createX509Certificate(certificateFactory, crt.getCertificate()))
.toArray(X509Certificate[]::new);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PemReader pemReader = new PemReader(new StringReader(sslConnectionProperties.getKey()));
PemObject pemObject = pemReader.readPemObject();
RSAPrivateKey privateKey = (RSAPrivateKey) keyFactory.generatePrivate(new PKCS8EncodedKeySpec(pemObject.getContent()));
keyStore.load(null);
keyStore.setKeyEntry(CERTIFICATE_ALIAS, privateKey, randomPasswordCallbackHandler.getPassword().toCharArray(), x509Certificates);
return keyStore;
}
Создать заголовок аутентификации для клиента CXF:
private WSS4JOutInterceptor createAuthenticationInterceptor(Merlin merlin, RandomPasswordCallbackHandler randomPasswordCallbackHandler) {
return new WSS4JOutInterceptor(new HashMap<String, Object>() {
{
put(WSHandlerConstants.ACTION, "Signature");
put(WSHandlerConstants.USER, CERTIFICATE_ALIAS);
put(WSHandlerConstants.PW_CALLBACK_REF, randomPasswordCallbackHandler);
put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");
put(ConfigurationConstants.SIG_PROP_REF_ID, SIGNATURE_PROPERTIES);
put(SIGNATURE_PROPERTIES, merlin);
}
});
}
@Bean
public TLSClientParameters tlsClientParameters(KeyStore keyStore, RandomPasswordCallbackHandler randomPasswordCallbackHandler) throws Exception {
String defaultAlgorithm = KeyManagerFactory.getDefaultAlgorithm();
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(defaultAlgorithm);
keyManagerFactory.init(keyStore, randomPasswordCallbackHandler.getPassword().toCharArray());
final TLSClientParameters tlsClientParameters = new TLSClientParameters();
tlsClientParameters.setKeyManagers(keyManagerFactory.getKeyManagers());
return tlsClientParameters;
}
Включение HTTPS на клиенте:
private void enableHttpsOnClient(Client client) {
HTTPConduit conduit = (HTTPConduit) client.getConduit();
conduit.setTlsClientParameters(tlsClientParameters);
}
Я прочитал, что вы можете динамически выбирать сертификат в соответствии с вашими требованиями. Или есть другой способ безопасного перехода на новый сертификат во время выполнения без потери времени работы?