Вопрос: Очистка кеша сертификатов Java (принудительная перезагрузка сертификатов)


Простой вопрос здесь.

Одно приложение дало мне это исключение при попытке получить доступ к веб-сайту с истекшим сертификатом: java.security.cert.CertificateExpiredException

Итак, я обновил сертификат с веб-сайта и перезапустил его. Когда я попытаюсь получить доступ к нему из Firefox или Chrome, он загрузит новый сертификат (дата его истечения установлена ​​где-то около 2040).

Проблема в том, что приложения Java, похоже, не обновляют этот сертификат, он, похоже, застрял в каком-то внутреннем кеше. Я уже пытался добавить его в keystore и установить параметры в свойствах приложения, например -Dcom.sun.net.ssl.checkRevocation=false, Независимо от того, что я делаю, это всегда бросает меня java.security.cert.CertificateExpiredException

Есть идеи?


6
2018-05-26 09:24


Источник


Привет, это сработало?
не работает для меня, сертификат сервера не кэшируется в хранилище ключей (по крайней мере, не в двух хранилищах, найденных в моей системе). Такая же проблема: веб-браузер показывает сертификат, действительный FROM может (новый сертификат), и java жалуется на сертификат, действительный UNTIL может (сертификат cached (?)). - user3338098
с помощью -Djavax.net.debug=SSL Я вижу, что он использует старый сертификат и что корневая цепочка сертификатов одинаков, это просто главный сертификат, устаревший (и истек). Может ли веб-сайт возвращать правильный сертификат моему браузеру, возвращая истекший в мое приложение java? (Кажется маловероятным) - user3338098
@ user3338098 - когда вы подключаетесь к приложению, используя что-то вроде openssl s_client -connect <host>:<port> показывает ли тот же самый «устаревший» сертификат, который вы видите при использовании браузера? - slm
@slm жаль, что не ответила, моя щедрость по этому вопросу заставила меня бежать от репутации, необходимой для комментариев. Ваш комментарий отправил меня в правильном направлении к решению. - user3338098


Ответы:


Месторасположение хранилища ключей по умолчанию в Java - это файл .keystore в вашем домашнем каталоге (свойство system.home), поэтому, если вы не укажете иное, где будет выглядеть приложение Java.

Попробуйте запустить:

$ keytool -list -keystore ~/.keystore -storepass changeit -v

чтобы проверить, существует ли сертификат с истекшим сроком действия.

Если вы хотите указать другое хранилище ключей для использования, то вы можете сделать это, используя следующие системные свойства:

javax.net.ssl.Keystore=/path/to/identity.jks
javax.net.ssl.keyStorePassword=mykeystorepassword

Я считаю, что Firefox использует NSS, и вы можете просмотреть его хранилище ключей с помощью утилиты certutil (из nss-инструментов или аналогичного пакета) - что-то вроде:

$ certutil -L -d sql:$HOME/.pki/nssdb

Вы должны иметь возможность использовать утилиту pk12util для извлечения ключа и сертификата в файл PKCS12, но вам, вероятно, лучше просто создать новый запрос подписи сертификата, используя утилиту keytool.

Обратите внимание, что отозванный сертификат не совпадает с истекшим, поэтому ваш checkRevocation = false не работает. CA может отозвать сертификат в любое время, даже если он еще не истек, и это указывает на то, что ему больше не нужно доверять.


1
2018-06-18 20:23



может не помочь, но есть также файл cacerts, который используется, путь подобен /usr/lib/jvm/java-version/jre/lib/security/cacerts в системах на основе unix - user3338098


Проблема заключается в том, что ошибка что приводит к тому, что HttpsUrlConnection не использует SNI, когда используется верификатор пользовательского имени хоста.

Обход проблемы

javax.net.ssl.HttpsURLConnection connection = (javax.net.ssl.HttpsURLConnection) new java.net.URL(url).openConnection();
connection.setHostnameVerifier(... hostname verifier which indirectly causes a bug ...);

//the remaining code fixes the bug by forcing the use of SNI even with the custom hostname verifier
final javax.net.ssl.SSLSocketFactory originalSocketFactory = connection.getSSLSocketFactory();
connection.setSSLSocketFactory(new javax.net.ssl.SSLSocketFactory() {
  public String[] getDefaultCipherSuites() {
    return originalSocketFactory.getDefaultCipherSuites();
  }

  public String[] getSupportedCipherSuites() {
    return originalSocketFactory.getSupportedCipherSuites();
  }

  private java.net.Socket convertSocket(javax.net.ssl.SSLSocket socket, String host) {
    javax.net.ssl.SNIHostName serverName = new javax.net.ssl.SNIHostName(host);
    java.util.List<javax.net.ssl.SNIServerName> serverNames = new java.util.ArrayList<>(1);
    serverNames.add(serverName);
    javax.net.ssl.SSLParameters params = socket.getSSLParameters();
    params.setServerNames(serverNames);
    socket.setSSLParameters(params);
    return socket;
  }

  public java.net.Socket createSocket(java.net.Socket s, String host, int port, boolean autoClose) throws IOException {

    //host = new URL(url).getHost();
    javax.net.ssl.SSLSocket socket = (javax.net.ssl.SSLSocket) originalSocketFactory.createSocket(s,host,port,autoClose);
    return convertSocket(socket, host);
  }

  public java.net.Socket createSocket(java.net.InetAddress host, int port) throws IOException {
    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(host, port);
  }

  public java.net.Socket createSocket(java.net.InetAddress address, int port, java.net.InetAddress localAddress, int localPort) throws IOException {
    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(address, port, localAddress, localPort);
  }

  public java.net.Socket createSocket(String host, int port) throws IOException {
    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(host, port);
  }

  public java.net.Socket createSocket(String host, int port, java.net.InetAddress localHost, int localPort) throws IOException {

    //You may need convertSocket here, I didn't
    return originalSocketFactory.createSocket(host, port, localHost, localPort);
  }
});

1
2018-06-21 01:49