Подтвердить что ты не робот

Использование настраиваемого хранилища доверия в Java, а также значение по умолчанию

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

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

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

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

Как получить несколько сертификатов SSL для сервера Java

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

4b9b3361

Ответ 1

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

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

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);

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


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

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

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