Как предоставить конкретное хранилище TrustStore при использовании хранилища ключей по умолчанию в Java (JSSE)

Обзор

JSSE позволяет пользователям предоставлять доверенные хранилища и хранилища ключей по умолчанию, задав параметры javax.net.ssl. *. Я хотел бы предоставить для своего приложения TrustManager не по умолчанию, позволяя пользователю указывать KeyManager как обычно, но, похоже, нет никакого способа добиться этого.

Подробности

http://docs.oracle.com/javase/7/docs/technotes/guides/security/jsse/JSSERefGuide.html#CustomizingStores

Предположим, на машинах unix я хочу разрешить пользователю использовать хранилище ключей pkcs12 для аутентификации, а на OS X я хочу разрешить пользователю использовать системную цепочку ключей. В OS X приложение можно запустить следующим образом:

java -Djavax.net.ssl.keyStore=NONE -Djavax.net.ssl.keyStoreType=KeychainStore \
     -Djavax.net.ssl.keyStorePassword=- -jar MyApplication.jar

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

Эта проблема

Теперь предположим, что я хочу связать самоподписанный центр сертификации с моим приложением. Я могу переопределить диспетчер доверия по умолчанию, создав TrustManagerFactory и передав хранилище ключей, содержащее мой сертификат (javadoc). Однако, чтобы использовать этот нестандартный диспетчер доверия, мне нужно создать и инициализировать SSLContext. Вот в чем проблема.

SSLContexts инициализируются вызовом init (..) и передать как KeyManager, так и TrustManager. Однако логика для создания KeyManager с использованием параметров javax.net.ssl. * Встроена в реализацию SSLContexts по умолчанию - я не могу найти способ получить KeyManager или KeyManagerFactory с использованием поведения по умолчанию, а также указать не по умолчанию TrustManager или TrustManagerFactory. Таким образом, кажется, что невозможно использовать, например, соответствующую реализацию цепочки ключей для конкретной операционной системы, одновременно предоставляя корневой сертификат для аутентификации удаленных серверов.


person Stephen Nelson    schedule 07.03.2013    source источник
comment
Чтобы уточнить, я не хочу повторно реализовывать поведение по умолчанию (например, путем копирования реализации sun.security.ssl.SSLContextImpl или javax.net.ssl.DefaultSSLContext), поскольку поведение загрузки по умолчанию может различаться на разных платформах и может изменение в будущих версиях JVM. Я специально ищу способ повторно использовать поведение по умолчанию, например. путем извлечения KeyManager из SSLContext по умолчанию.   -  person Stephen Nelson    schedule 08.03.2013


Ответы (2)


Похоже, вы столкнулись с проблемой, аналогичной этому вопросу, поскольку использование null для параметра trustmanager в SSLContext.init(...) возвращается к диспетчер доверия по умолчанию, а не диспетчер ключей.

При этом не так сложно инициализировать KeyManager, используя системные свойства по умолчанию. Что-то вроде этого должно работать (код написан непосредственно в этом ответе, поэтому вам может потребоваться исправить несколько мелочей):

String provider = System.getProperty("javax.net.ssl.keyStoreProvider");
String keystoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
KeyStore ks = null;
if (provider != null) {
    ks = KeyStore.getInstance(keystoreType, provider);
} else {
    ks = KeyStore.getInstance(keystoreType);
}
InputStream ksis = null;
String keystorePath = System.getProperty("javax.net.ssl.keyStore");
String keystorePassword = System.getProperty("javax.net.ssl.keyStorePassword");
if (keystorePath != null && !"NONE".equals(keystorePath)) {
    ksis = new FileInputStream(keystorePath);
}
try {
    ks.load(ksis, keystorePassword.toCharArray());
} finally {
     if (ksis != null) { ksis.close(); }
}

KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
kmf.init(ks, keystorePassword.toCharArray());
// Note that there is no property for the key password itself, which may be different.
// We're using the keystore password too.

SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(kmf.getKeyManagers(), ..., null);

(Этот служебный класс также может представлять интерес, в частности getKeyStoreDefaultLoader().)

РЕДАКТИРОВАТЬ: (после вашего дополнительного комментария)

Боюсь, что для Oracle и IBM JSSE не существует поведения по умолчанию, если вы хотите настроить только половину SSLContext. Раздел, на который вы ссылаетесь в документации Oracle JSSE, говорит: «Если хранилище ключей указано системным свойством javax.net.ssl.keyStore и соответствующим системным свойством javax.net.ssl.keyStorePassword, тогда KeyManager, созданный SSLContext по умолчанию будет реализацией KeyManager для управления указанным хранилищем ключей. "На самом деле это не применимо здесь, так как вы используете пользовательский SSLContext, а не по умолчанию в любом случае (даже если вы настраиваете часть Это).

В любом случае справочное руководство Oracle JSSE и справочное руководство IBM JSSE различаются по этому вопросу. (Я не уверен, сколько из этого должно быть «стандартным» и должно ли одно в принципе соответствовать другому, но это явно не так.)

Оба раздела «Создание SSLContext объекта» почти идентичны, но они разные.

Справочное руководство по Oracle JSSE говорит:

Если параметр KeyManager [] имеет значение null, тогда для этого контекста будет определен пустой KeyManager.

Справочное руководство по IBM JSSE говорит:

Если параметр KeyManager [] имеет значение null, в установленных поставщиках безопасности будет производиться поиск реализации KeyManagerFactory с наивысшим приоритетом, из которой будет получен соответствующий KeyManager.

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

person Bruno    schedule 07.03.2013
comment
Не идеальный ответ, потому что он требует повторной реализации поведения по умолчанию, а не его повторного использования, но тем не менее полезен. Спасибо за подсказку о jsslutils. - person Stephen Nelson; 08.03.2013
comment
К сожалению, нет возможности повторно использовать половину контекста SSL. Спасибо за подробный ответ. - person Stephen Nelson; 27.03.2013

Нетрудно написать KeyManager с поведением по умолчанию. Это всего лишь несколько строк кода. Удивительно, что не все SSLContexts ведут себя так же с. KeyManager, как и в случае с TrustManager. IBM JSSE действительно так себя ведет. Но синтезировать самому несложно:

SSLContext  context = SSLContext.getInstance("TLS");
String  keyStore = System.getProperty("javax.net.ssl.keyStore");
String  keyStoreType = System.getProperty("javax.net.ssl.keyStoreType", KeyStore.getDefaultType());
String  keyStorePassword = System.getProperty("javax.net.ssl.keyStorePassword","");
KeyManager[]    kms = null;
if (keyStore != null)
{
    KeyManagerFactory   kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    KeyStore    ks = KeyStore.getInstance(keyStoreType);
    if (keyStore != null && !keyStore.equals("NONE")) {
        fs = new FileInputStream(keyStore);
    ks.load(fs, keyStorePassword.toCharArray());
    if (fs != null)
        fs.close();
    char[]  password = null;
    if (keyStorePassword.length() > 0)
        password = keyStorePassword.toCharArray();
    kmf.init(ks,password);
    kms = kmf.getKeyManagers();
}
context.init(kms,null,null);
person user207421    schedule 07.03.2013
comment
Как вы заметили, разные реализации JSSE ведут себя по-разному, я не хочу реализовывать свой собственный KeyManager, а затем тестировать его на всех возможных комбинациях JVM, архитектуры и реализации JSSE. Реализация JDK7 по умолчанию, кстати, не превышает 100 строк кода. - person Stephen Nelson; 08.03.2013
comment
@StephenNelson Хм, мне 21: - | - person user207421; 08.03.2013
comment
У вас нет комментариев и нет обработки PKCS11 :-) В остальном это полезно, спасибо. - person Stephen Nelson; 27.03.2013
comment
@StephenNelson Он должен обрабатывать PKCS # 11, если указан javax.net.ssl.keyStoreType. - person user207421; 23.04.2013