Как проверить отозванный сертификат в соединениях на основе SSL с использованием LDAP-сервера в памяти (из UnboundID LDAP SDK для Java)?
В своем приложении я использую библиотеку UnboundID LDAP SDK для Java (https://ldap.com/unboundid-ldap-sdk-for-java/).
Я пытаюсь написать тест, который точно проверит, как соединения LDAPS / StartTLS ведут себя для сертификатов, которые были правильно настроены, но когда запрос к серверу LDAP выполнен, они больше не действительны (например, ключи были скомпрометированы, и мы заменил на другой).
Я хотел проверить это поведение с помощью интеграционных тестов, используя сервер в памяти из UnboundID LDAP SDK - InMemoryDirectoryServer (https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/listener/InMemoryDirectoryServer.html)
Постараюсь пошагово описать, как это выглядит на производстве:
- Мы создаем пул подключений к нашему серверу, используя действующий сертификат. У нас есть рабочий и настроенный
LDAPConnectionPool
объект (https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/sdk/LDAPConnectionPool.html). Подключение работает через порт 636 по протоколу LDAPS или через порт 389 по протоколу StartTLS. - На стороне сервера LDAP мы отзываем сертификат, потому что, например, ключи были скомпрометированы. Мы генерируем новый сертификат. Подключение к LDAP на стороне нашего приложения все еще активно.
- Когда мы выполняем простой поиск из нашего приложения -
ldapConnectionPool.search(someSearchRequest());
мы должны получитьLDAPSearchException
, сообщая нам, что наше соединение недействительно.
Допустим, опция насмешки LDAPConnectionPool не учитывается.
Конфигурация моего сервера в памяти следующая:
class InMemoryLDAPServer {
private final static String LDAPS_LISTENER_NAME = "LDAPS";
private final static String TLS_LISTENER_NAME = "TLS";
private final static String BASE_DN = "dc=example,dc=com";
private final static String LDIF_FILENAME = "ldap.ldif";
private final InMemoryDirectoryServer directoryServer;
private InMemoryLDAPServer(InMemoryDirectoryServer directoryServer) {
this.directoryServer = directoryServer;
}
static InMemoryLDAPServer newSecureServer(final InMemoryOperationInterceptor interceptor) throws Exception {
InMemoryDirectoryServerConfig config = createServerConfig(interceptor);
return getEmbeddedLdapServer(config);
}
private static InMemoryLDAPServer getEmbeddedLdapServer(final InMemoryDirectoryServerConfig config) throws LDAPException {
InMemoryDirectoryServer directoryServer = new InMemoryDirectoryServer(config);
directoryServer.importFromLDIF(true, ldifFilename());
directoryServer.startListening();
return new InMemoryLDAPServer(directoryServer);
}
private static InMemoryDirectoryServerConfig createServerConfig(final InMemoryOperationInterceptor interceptor) throws LDAPException, GeneralSecurityException {
final SSLUtil serverSSLUtil = new SSLUtil(KeyStoreStub.keyStores(), KeyStoreStub.trustStores());
final SSLUtil clientSSLUtil = new SSLUtil(KeyStoreStub.trustStores());
InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(BASE_DN);
config.setSchema(null);
config.setListenerConfigs(InMemoryListenerConfig.createLDAPSConfig(LDAPS_LISTENER_NAME,
InetAddress.getLoopbackAddress(), 0, serverSSLUtil.createSSLServerSocketFactory(),
clientSSLUtil.createSSLSocketFactory()), InMemoryListenerConfig.createLDAPConfig(TLS_LISTENER_NAME, InetAddress.getLoopbackAddress(), 0, serverSSLUtil.createSSLSocketFactory()));
config.addInMemoryOperationInterceptor(interceptor);
return config;
}
private static String ldifFilename() {
return getPath(LDIF_FILENAME);
}
private static String getPath(final String relativePath) {
File resourcesDirectory = new File(PATH_NAME + relativePath);
return resourcesDirectory.getAbsolutePath();
}
}
Моя идея - изменить возвращаемый результат, используя InMemoryOperationInterceptor
(https://docs.ldap.com/ldap-sdk/docs/javadoc/com/unboundid/ldap/listener/interceptor/InMemoryOperationInterceptor.html). К сожалению, это не совсем так.
Образец теста:
@Test
void shouldReturnEmptyResultWhenConfigurationIsBroken() throws Exception {
//given
TestOperationInterceptor testOperationInterceptor = new TestOperationInterceptor();
InMemoryLDAPServer.newSecureServer(testOperationInterceptor);
final LdapTestConfiguration testConfiguration = createTestLDAPConfiguration();
//when
final List<UserResult> results = ldapSearchService.searchByUsername(testConfiguration.getUsername());
//then
assertTrue(results.isEmpty());
}
Образец перехватчика:
class TestOperationInterceptor extends InMemoryOperationInterceptor {
@Override
public void processSearchResult(final InMemoryInterceptedSearchResult result) {
final LDAPSearchException searchException = new LDAPSearchException(ResultCode.NO_SUCH_OBJECT, "Provided keystore is revoked or expired");
final SearchResult searchResult = new SearchResult(searchException);
result.setResult(searchResult);
}
}
У вас есть еще идеи, как решить эту проблему?