Как поддерживать несколько TrustStore в клиентском приложении java SSL

В нашем java-приложении нам нужно общаться со списком серверов по SSL, используя протокол https. Список серверов для связи будет меняться во время выполнения. Изначально у нас нет ни одного сертификата сервера. Во время выполнения мы получим новый сертификат сервера и добавим сертификат открытого ключа в хранилище доверенных сертификатов; и любое новое https-соединение с сервером должно использовать обновленное хранилище доверенных сертификатов.

Мы думаем, что нам следует использовать два хранилища доверия, одно cacerts (по умолчанию поставляется с jre) и другое, содержащее сертификаты серверов, которые мы добавляем/удаляем динамически в списке. Это гарантирует, что мы не изменим TrustStore (cacerts) по умолчанию для java.

Пожалуйста, предложите, как этого можно добиться. Кроме того, есть ли способ использовать определенное хранилище доверенных сертификатов только для определенного потока в java, чтобы другие (существующие и новые) потоки по-прежнему использовали java trueststore по умолчанию (cacerts), а один конкретный поток будет использовать конкретное хранилище доверенных сертификатов для сервер.

Спасибо, Дипак


person user443180    schedule 09.09.2010    source источник


Ответы (2)


Если вы хотите динамически импортировать сертификат, вам может потребоваться использовать пользовательский файл x509TrustManager. Это делается при настройке SSLContext. , который сам используется для создания SSLSocketFactory или SSLEngine.

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

Это будет происходить в следующих направлениях:

PKIXSSLContextFactory sslContextFactory = new PKIXSSLContextFactory();
sslContextFactory.setTrustManagerWrapper(new X509TrustManagerWrapper() {
    @Override
    public X509TrustManager wrapTrustManager(final X509TrustManager origManager) {
        return new X509TrustManager() { 
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return origManager.getAcceptedIssuers();
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain,
                                                   String authType)
                    throws CertificateException {
                try {
                    // This will call the default trust manager
                    // which will throw an exception if it doesn't know the certificate
                    origManager.checkServerTrusted(chain, authType);
                } catch (CertificateException e) {
                    // If it throws an exception, check what this exception is
                    // the server certificate is in chain[0], you could
                    // implement a callback to the user to accept/refuse
                }
            }

            @Override
            public void checkClientTrusted(X509Certificate[] chain,
                                                   String authType)
                    throws CertificateException {
                origManager.checkClientTrusted(chain, authType);
            }
        };
    }
});
SSLContext sslContext = sslContextFactory.buildSSLContext();

((PKIX)SSLContextFactory и X509TrustManagerWrapper взяты из jSSLutils, но остальные доступны с J2SE/J2EE.)

Есть несколько CertificateExceptions, которые вы может захотеть поймать (см. подклассы). Если вы делаете обратный вызов пользователю, возможно, что соединение SSL/TLS не будет выполнено в первый раз из-за тайм-аута рукопожатия SSL/TLS (если ответ на обратный вызов занимает слишком много времени).

Затем вы можете использовать этот SSLContext по умолчанию, используя SSLContext.setSSLContext(...) (из Java 6), но это не обязательно хорошая идея. Если можете, передайте SSLContext библиотеке, которая устанавливает соединение SSL/TLS. То, как это делается, различается, но, например, Apache HTTP Client 4.x имеет несколько вариантов настройки параметров SSL, один из которых — путем передачи KeyStores, а другой — путем передачи SSLContext.

Вы также можете сделать что-то для каждого потока, а не для каждого объекта, который будет подключаться (зависит от библиотеки), проверив текущий поток в X509TrustManager: это, вероятно, немного усложнит ситуацию с точки зрения синхронизации и управления потоками / «осведомленности» на доверительный управляющий.

person Bruno    schedule 09.09.2010

Этот вопрос настолько стар, что я сомневаюсь, что моя битка кому-нибудь поможет, но вот...

Если вы хотите решить проблему OP (исходный плакат), не прибегая к изменениям кода, вы можете настроить свою JVM (я тестировал только с Tomcat) для поддержки желаемой конфигурации OP:

  1. оставьте «упакованный» файл JDK cacerts в покое
  2. импортируйте свои сертификаты в отдельный файл, и ваши приложения JAVA «доверяют» им

Раньше я просто импортировал свой дополнительный сертификат в отдельный файл, а затем с большим успехом ссылался на него при запуске JVM с параметром -Djavax.net.ssl.trustStore=$JAVA_HOME/jre/lib/security/jssecacerts, но я предполагаю, что недавние (несколько) проблемы с безопасностью JAVA изменили автоматическое включение файла cacerts, распространяемого с SDK. .

Итак, я нашел отличное решение, используя информацию из этого поста и этих страниц (с некоторыми незначительными изменениями):

Что я делал:

  • установите параметр JVM trustStore в мой отдельный файл хранилища ключей (в который я бы импортировал дополнительные сертификаты) следующим образом

Что я делаю сейчас:

  • Установите параметр trustStore в «упакованный» файл cacerts.
  • Установите параметр keyStore в мой файл «дополнительных сертификатов».
  • Установите для параметра keyStorePassword пароль моего хранилища ключей (по умолчанию — changeit).

На что это похоже:

-Djavax.net.ssl.trustStore=$JAVA_HOME/jre/lib/security/cacerts \
-Djavax.net.ssl.keyStore=$JAVA_HOME/jre/lib/security/jssecacerts \
-Djavax.net.ssl.keyStorePassword="changeit" \

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

person William Crighton    schedule 26.01.2013
comment
Вопрос заключался в том, чтобы сделать это для определенного потока/соединения, тогда как установка javax.net.ssl.trustStore повлияет на все соединения (кроме тех, у которых есть свои конкретные SSLContext). Кроме того, в этом случае вообще нет смысла устанавливать javax.net.ssl.keyStore*jssecacerts не должен содержать закрытый ключ, поэтому в любом случае будет бесполезен в качестве хранилища ключей). - person Bruno; 26.01.2013