Выбор SSL-сертификата клиента в Java - PullRequest
25 голосов
/ 14 сентября 2010

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

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

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

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

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

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

Ответы [ 3 ]

14 голосов
/ 15 сентября 2010

Конфигурирование выполняется через SSLContext, что фактически является фабрикой для 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 по вашему выбору (см. этот вопрос ).

Только что понял, что вы говорили об 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.

8 голосов
/ 15 сентября 2010

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

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

0 голосов
/ 24 июня 2011

Я инициализировал 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));

Работает как шарм!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...