Здесь простой вопрос.
Одно приложение выдало мне это исключение при попытке доступа к веб-сайту с истекшим сертификатом: java.security.cert.CertificateExpiredException
Итак, я обновил сертифицированный компьютер с веб-сайта и перезапустил его. Когда я пытаюсь получить к нему доступ из Firefox или Chrome, он загружает новый сертификат (срок действия которого установлен где-то около 2040 года).
Проблема в том, что приложения Java, похоже, не обновляют этот сертификат, он застрял во внутреннем кэше. Я уже пробовал добавить его в keystore
и установите параметры в свойствах приложения, например -Dcom.sun.net.ssl.checkRevocation=false
. Независимо от того, что я делаю, это всегда вызывает у меня java.security.cert.CertificateExpiredException
Любые идеи?
Проблема, оказывается, связана с ошибка что заставляет 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);
}
});
Расположение хранилища ключей Java по умолчанию - это файл .keystore в вашем домашнем каталоге (системное свойство user.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-tools или аналогичного пакета) - что-то вроде:
$ certutil -L -d sql:$HOME/.pki/nssdb
Вы должны иметь возможность использовать утилиту pk12util для извлечения ключа и сертификата в файл PKCS12, но вам, вероятно, лучше просто создать новый запрос на подпись сертификата с помощью утилиты keytool.
Обратите внимание, что отозванный сертификат - это не то же самое, что сертификат с истекшим сроком действия, поэтому ваш checkRevocation = false не работает. ЦС может отозвать сертификат в любое время, даже если срок его действия еще не истек, и это означает, что ему больше нельзя доверять.
Я столкнулся с этим вопросом, когда искал способ заставить Java распознавать недавно импортированные сертификаты.
Для меня причина, по которой сертификаты не обновлялись (в Windows), заключалась в том, что уже был запущен экземпляр Java (предположительно со старым кешем сертификатов).
Я просто убил старый процесс Java, и в следующий раз, когда он автоматически загрузился (из-за запущенных процессов Gradle), сертификаты были обновлены.