Использование пользовательского 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 справочные страницы, это ваша ответственность, чтобы поддерживать (то есть, добавить и удалить) сертификаты, содержащиеся в этом файле, если вы используете эту файл как хранилище.
в зависимости от конфигурации сертификата серверов, которые вы контакт, вам может потребоваться добавить дополнительные корневые сертификаты. Получить необходимы определенные корневые сертификаты от соответствующего поставщика.