Использование пользовательского truststore в java, а также по умолчанию

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

одним из решений, которое я нашел, было добавить сертификаты из trustStore по умолчанию в свой собственный. Однако мне не нравится это решение, потому что оно требует от меня продолжать управлять этим trustStore. (Я не могу предположить, что эти сертификаты останутся статичными в обозримом будущем, верно?)

кроме того, я нашел два 5-летних потока с аналогичной проблемой:

регистрация нескольких хранилища ключей в JVM

как я могу иметь несколько сертификатов SSL для сервера Java

Они оба углубляются в инфраструктуру JAVA SSL. Я надеялся, что теперь есть более удобное решение, которое я могу легко объяснить в обзоре безопасности моего кода.

1 ответов


вы можете использовать аналогичный шаблон для того, что я упомянул в предыдущий ответ (по другой проблеме).

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

TrustManagerFactory tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
// Using null here initialises the TMF with the default trust store.
tmf.init((KeyStore) null);

// Get hold of the default trust manager
X509TrustManager defaultTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        defaultTm = (X509TrustManager) tm;
        break;
    }
}

FileInputStream myKeys = new FileInputStream("truststore.jks");

// Do the same with your trust store this time
// Adapt how you load the keystore to your needs
KeyStore myTrustStore = KeyStore.getInstance(KeyStore.getDefaultType());
myTrustStore.load(myKeys, "password".toCharArray());

myKeys.close();

tmf = TrustManagerFactory
    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(myTrustStore);

// Get hold of the default trust manager
X509TrustManager myTm = null;
for (TrustManager tm : tmf.getTrustManagers()) {
    if (tm instanceof X509TrustManager) {
        myTm = (X509TrustManager) tm;
        break;
    }
}

// Wrap it in your own class.
final X509TrustManager finalDefaultTm = defaultTm;
final X509TrustManager finalMyTm = myTm;
X509TrustManager customTm = new X509TrustManager() {
    @Override
    public X509Certificate[] getAcceptedIssuers() {
        // If you're planning to use client-cert auth,
        // merge results from "defaultTm" and "myTm".
        return finalDefaultTm.getAcceptedIssuers();
    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        try {
            finalMyTm.checkServerTrusted(chain, authType);
        } catch (CertificateException e) {
            // This will throw another CertificateException if this fails too.
            finalDefaultTm.checkServerTrusted(chain, authType);
        }
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain,
            String authType) throws CertificateException {
        // If you're planning to use client-cert auth,
        // do the same as checking the server.
        finalDefaultTm.checkClientTrusted(chain, authType);
    }
};


SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[] { customTm }, null);

// You don't have to set this as the default context,
// it depends on the library you're using.
SSLContext.setDefault(sslContext);

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


это, как говорится, в принципе, вам всегда придется обновлять truststore по мере необходимости. Справочное руководство Java 7 JSSE имело "важное примечание" об этом, теперь пониженное до "примечание" в версии 8 того же руководства:

JDK поставляется с ограниченным количеством доверенных корневых сертификатов в файл java-home/lib/security / cacerts. Как задокументировано в keytool справочные страницы, это ваша ответственность, чтобы поддерживать (то есть, добавить и удалить) сертификаты, содержащиеся в этом файле, если вы используете эту файл как хранилище.

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