2010-09-14 8 views
24

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

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

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

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

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

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

ответ

7

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

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

+0

К сожалению, у нас нет контроля над серверами. Я также надеялся, что эти подсказки могут быть полезны, но тест показал, что, по крайней мере, с одним сервером их не было. WRT Axis2, который также был моим опытом. Если вам известно о другом инструменте Java, который допускает такое поведение, я бы очень признателен за ваше разделение; мы с удовольствием рассмотрим его. – Carlos

+0

@ Карлос - Может, я не был ясен. Вам не нужен какой-либо контроль над сервером; если вашему партнеру теперь требуется сертификат клиента, они будут настраивать свой сервер для его запроса. Ваш клиент будет * не * отправлять этот сертификат на другие серверы, потому что они (предположительно) не запрашивают его. Единственная потенциальная проблема заключается в том, что если дополнительные службы начнут требовать сертификат клиента, но не будут принимать те же ЦС. – erickson

+0

Получил, спасибо. Наверное, другой вопрос остается.Если завтра мы добавим еще одну веб-службу, которая также требует сертификата клиента, есть ли способ выбрать, какой сертификат в хранилище ключей должен присутствовать для каждой службы? – Carlos

14

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

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

Если это поможет, вы можете найти с помощью jSSLutils (and its wrapper) для конфигурации вашего SSLContext (это в основном вспомогательные классы для сборки SSLContext). (Обратите внимание, что этот пример для выбора на стороне сервера псевдоним, но он может быть адаптирован к source code is available.)

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

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

В качестве альтернативы, в соответствии с this Axis 2 document, он выглядит как Ось 2 использует Apache HTTP Client 3.x:

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

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

+0

Hum ... и просто понял, что вы используете Java 1.5, так что нет SSLContext.setDefault (...) 'в любом случае. Почему бы не использовать Java 6? Насколько я знаю, Java 5 больше не поддерживается (для разных патчей безопасности не было вреда). – Bruno

+0

(Установка «SSLContext» через Apache HTTP Client 3.x должна работать с Java 1.5.) – Bruno

+0

Спасибо за вашу помощь! – Carlos

0

Я инициализируется 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)); 

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

+1

Можете ли вы указать импорт? EasySSLProtocolSocketFactory? KeyMaterial? Полный пример с зависимостями был бы очень приятным. Благодарю. – marcolopes

+0

Было бы неплохо, если бы вы могли более подробно рассказать о своем коде. –

+0

Посмотрите этот файл на github, чтобы ответить на большинство ваших вопросов: https://github.com/nickman/helios/blob/gh-pages/helios-collectors/collectors-http/src/main/java/org/ Helios/коллекторы/URL/URLCollector.java – helios

Смежные вопросы