Включить проверку отзыва сертификата SSL в OpenJDK 11

Есть ли какой-нибудь быстрый «декларативный» способ в Java 11 вместо утомительной ручной реализации, позволяющий проверить, отозван ли сертификат?

Я пытался использовать свойства из этого ответа: >Проверьте статус отзыва сертификата X509 в Spring-Security перед аутентификацией с помощью этого фиктивного отозванного сертификата: https://revoked.badssl.com, но код всегда принимает сертификат. Я что-то не так делаю или эти свойства уже не актуальны для Java 11? Если да, то есть ли у нас альтернативы?

Ниже мой код:

public static void validateOnCertificateRevocation(boolean check) {
    if (check) {
        System.setProperty("com.sun.net.ssl.checkRevocation", "true");
        System.setProperty("com.sun.security.enableCRLDP", "true");

        Security.setProperty("ocsp.enable", "true");
    }

    try {
        new URL("https://revoked.badssl.com").openConnection().connect();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

person Roman Khomyshynets    schedule 25.03.2019    source источник


Ответы (1)


Похоже, что эти параметры должны быть установлены до того, как будет выполнен первый запрос.

Поэтому следующий код в виде отдельной Java-программы выдает CertPathValidatorException: Certificate has been revoked (проверено с использованием OpenJDK 11.0.2 x64 в Windows):

public static void main(String[] args) {
    validateOnCertificateRevocation(true); // throws CertPathValidatorException
}

Однако следующий код не вызывает ошибок/исключений:

public static void main(String[] args) {
    validateOnCertificateRevocation(false);
    validateOnCertificateRevocation(true); // nothing happens
}

Вы можете видеть, что изменение параметров после обработки первого запроса неэффективно. Я предполагаю, что эти параметры обрабатываются в блоке static { ... } некоторого класса, связанного с проверкой сертификата.

Если вы по-прежнему хотите включить/отключить проверку отзыва сертификата для каждого запроса, вы можете сделать это, внедрив собственный X509TrustManager, который использует CertPathValidator (для которого вы можете включить/отключить проверку отзыва сертификата через PKIXParameters.setRevocationEnabled(boolean).

В качестве альтернативы есть решение для глобального включения проверки отзыва сертификата и явной обработки CertificateRevokedException:

private boolean checkOnCertificateRevocation;

@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException {
    try {
        getDefaultTrustManager().checkServerTrusted(certs, authType);
    } catch (CertificateException e) {
        if (checkOnCertificateRevocation) {
            if (getRootCause(e) instanceof CertificateRevokedException) {
                throw e;
            }
        }
    }
}
person Robert    schedule 26.03.2019
comment
Благодарю вас! Свойство com.sun.net.ssl.checkRevocation на самом деле, как вы предполагали, обрабатывается только один раз при инициализации статического поля checkTLSRevocation в классе sun.security.validator.PKIXValidator. Таким образом, в качестве обходного пути мы всегда можем оставить это свойство включенным и перехватывать/возвращать исключение на основе логического флага в клиентском коде. Но, может быть, существует какое-то более элегантное решение для включения/отключения проверки отзыва в каждом запросе? - person Roman Khomyshynets; 26.03.2019
comment
Вы можете реализовать собственный X509TrustManager, который использует CertPathValidator для проверки сертификата. Используемый класс PKIXParameters имеет метод с именем setRevocationEnabled(boolen), который может включать/отключать проверку отзыва сертификата. Это должно работать на уровне каждого запроса. - person Robert; 26.03.2019
comment
Ценю вашу помощь! Однако после нескольких попыток реализовать слишком сложный CertPathValidator было решено придерживаться принципа «всегда бросай, лови, когда нужно». Окончательный код пользовательского X509TrustManager теперь выглядит как @Override public void checkServerTrusted(X509Certificate[] certs, String authType) throws CertificateException { try { getDefaultTrustManager().checkServerTrusted(certs, authType); } catch (CertificateException e) { if (checkOnCertificateRevocation) { if (getRootCause(e) instanceof CertificateRevokedException) { throw e; } } } } - person Roman Khomyshynets; 28.03.2019
comment
@Roman Я обновил свой ответ и включил наше обсуждение из комментариев. - person Robert; 28.03.2019