Подтвердить что ты не робот

Выбор SSL-сертификата клиента в Java

Наша система взаимодействует с несколькими поставщиками веб-сервисов. Все они вызывается из одного клиентского приложения Java. Все веб-службы до сих пор прошли через SSL, но ни один из них не использует клиентские сертификаты. Ну, новый партнер меняет это.

Сделать приложение использовать сертификат для вызова очень просто; настройка javax.net.ssl.keyStore и javax.net.ssl.keyStorePassword сделает это. Однако проблема заключается в том, как сделать так, чтобы он использовал только сертификат при вызове этой конкретной веб-службы. Я предполагаю, что в более общем плане мы хотели бы иметь возможность выбирать сертификат клиента, который будет использоваться, если таковой имеется.

Одним быстрым решением может быть настройка свойств системы, вызов методов и их снятие. Единственная проблема заключается в том, что мы имеем дело с многопоточным приложением, поэтому теперь нам нужно будет иметь дело с синхронизацией или блокировками или тем, что у вас есть.

Каждый клиент службы должен быть полностью независим друг от друга, и они индивидуально упакованы в отдельные JAR. Таким образом, один из вариантов, который произошел со мной (хотя мы не проанализировали его должным образом) заключается в том, чтобы каким-то образом изолировать каждый JAR, возможно, загружать каждый из них под другую виртуальную машину с разными параметрами. Это просто идея, что я не знаю, как реализовать (или если это возможно, если на то пошло.)

В этом сообщении предлагается, чтобы можно было выбрать отдельный сертификат из хранилища ключей, но как присоединить его к запросу, кажется, совсем другая проблема.

Мы используем классы Java 1.5, Axis2 и клиента, созданные с помощью wsimport или wsdl2java.

4b9b3361

Ответ 1

Java-SSL-клиенты будут отправлять сертификат только по запросу сервера. Сервер может отправить необязательный намек о том, какие сертификаты он примет; это поможет клиенту выбрать один сертификат, если он имеет несколько.

Обычно создается новый SSLContext с определенным сертификатом клиента, а Socket экземпляры создаются из factory, полученных из этого контекста. К сожалению, Axis2, похоже, не поддерживает использование SSLContext или пользовательского SocketFactory. Его настройки сертификата клиента глобальны.

Ответ 2

Конфигурация выполняется с помощью SSLContext, которая фактически является factory для SSLSocketFactory (или SSLEngine). По умолчанию это будет настроено из свойств javax.net.ssl.*. Кроме того, когда сервер запрашивает сертификат, он отправляет сообщение TLS/SSL CertificateRequest, которое содержит список отличительных имен CA, которые он хочет принять. Хотя этот список строго говоря только ориентировочный (то есть серверы могут принимать сертификаты от эмитентов, не входящих в список, или могут отказаться от действительных сертификатов из ЦС в списке), он обычно работает таким образом.

По умолчанию выбор сертификата в X509KeyManager, настроенном в SSLContext (опять вам не нужно беспокоиться об этом), выберет один из сертификатов, который был выпущен одним из них в списке ( или может быть привязана к эмитенту там). Этот список является параметром issuers в X509KeyManager.chooseClientAlias (alias является псевдонимом для сертификата, который вы хотите выбрать, так как упомянутый в хранилище ключей). Если у вас несколько кандидатов, вы также можете использовать параметр socket, который даст вам IP-адрес однорангового узла, если это поможет сделать выбор.

Если это поможет, вы можете найти jSSLutils (и его обертку) для конфигурации вашего SSLContext (это в основном вспомогательные классы для сборки SSLContext s). (Обратите внимание, что этот пример предназначен для выбора псевдонима на стороне сервера, но его можно адаптировать, доступен .

После того, как вы это сделали, вы должны найти документацию о системном свойстве axis.socketSecureFactory в Axis (и SecureSocketFactory). Если вы посмотрите на исходный код Axis, не сложно создать org.apache.axis.components.net.SunJSSESocketFactory, инициализированный из SSLContext по вашему выбору (см. этот вопрос).забастовкa >

Просто понял, что вы говорили о Axis2, где SecureSocketFactory, кажется, исчез. Возможно, вам удастся найти обходной путь с использованием стандартного SSLContext, но это повлияет на все ваше приложение (что не очень удобно). Если вы используете X509KeyManagerWrapper jSSLutils, вы можете использовать значение по умолчанию X509KeyManager и рассматривать только определенные хосты как исключение. (Это не идеальная ситуация, я не уверен, как использовать пользовательский SSLContext/SSLSocketFactory в Axis 2.)

В качестве альтернативы, согласно этот документ Axis 2, похоже, что Axis 2 использует Apache HTTP Client 3.x:

Если вы хотите выполнить SSL-клиент аутентификации (двухсторонний SSL), вы можете используйте протокол Protocol.registerProtocol функция HttpClient. Ты можешь перезаписать протокол "https" или использовать другой протокол для вашего SSL клиентская аутентификация если вы не хотите HTTPS. Найти больше информации на http://jakarta.apache.org/commons/httpclient/sslguide.html

В этом случае SslContextedSecureProtocolSocketFactory должен помочь вам настроить SSLContext.

Ответ 3

Я инициализировал EasySSLProtocolSocketFactory и экземпляры протокола для разных конечных точек и зарегистрировал протокол с помощью уникального ключа следующим образом:

/**
 * This method does the following:
 * 1. Creates a new and unique protocol for each SSL URL that is secured by client certificate
 * 2. Bind keyStore related information to this protocol
 * 3. Registers it with HTTP Protocol object 
 * 4. Stores the local reference for this custom protocol for use during furture collect calls
 * 
 *  @throws Exception
 */
public void registerProtocolCertificate() throws Exception {
    EasySSLProtocolSocketFactory easySSLPSFactory = new EasySSLProtocolSocketFactory();
    easySSLPSFactory.setKeyMaterial(createKeyMaterial());
    myProtocolPrefix = (HTTPS_PROTOCOL + uniqueCounter.incrementAndGet());
    Protocol httpsProtocol = new Protocol(myProtocolPrefix,(ProtocolSocketFactory) easySSLPSFactory, port);
    Protocol.registerProtocol(myProtocolPrefix, httpsProtocol);
    log.trace("Protocol [ "+myProtocolPrefix+" ] registered for the first time");
}

/**
 * Load keystore for CLIENT-CERT protected endpoints
 */
private KeyMaterial createKeyMaterial() throws GeneralSecurityException, Exception  {
    KeyMaterial km = null;
    char[] password = keyStorePassphrase.toCharArray();
    File f = new File(keyStoreLocation);
    if (f.exists()) {
        try {
            km = new KeyMaterial(keyStoreLocation, password);
            log.trace("Keystore location is: " + keyStoreLocation + "");
        } catch (GeneralSecurityException gse) {
            if (logErrors){
                log.error("Exception occured while loading keystore from the following location: "+keyStoreLocation, gse);
                throw gse;
            }
        }
    } else {
        log.error("Unable to load Keystore from the following location: " + keyStoreLocation );
        throw new CollectorInitException("Unable to load Keystore from the following location: " + keyStoreLocation);
    }
    return km;
}   

Когда мне приходится ссылаться на веб-службу, я делаю это (которые в основном заменяют "https" в URL-адресе https1 или https2 или что-то еще в зависимости от протокола, который вы инициализировали для этой конкретной конечной точки):

httpClient.getHostConfiguration().setHost(host, port,Protocol.getProtocol(myProtocolPrefix));
initializeHttpMethod(this.url.toString().replace(HTTPS_PROTOCOL, myProtocolPrefix));

Он работает как шарм!