Java / Keystore Проверить подписанный сертификат

Я работаю над аутентификацией сертификата клиента между встроенным сервером причала и клиентом. Оба они используют хранилище ключей. Сертификат клиента подписан сертификатом сервера, который подписан ЦС. Jetty использует 2 метода для аутентификации клиентского сертификата, javax.net.ssl.SSLEngine, который, кажется, работает, и они также используют приведенный выше код.

List<X509Certificate> certList = Certificate chain sent by the client
KeyStore truststore = server's truststore

//No use of CRL/OSCP/CRLDP
_crls = null;
_enableOCSP = false;
_enableCRLDP = false;

try{
 X509CertSelector certSelect = new X509CertSelector();
 certSelect.setCertificate((X509Certificate) certList.get(0));

 // Configure certification path builder parameters
 PKIXBuilderParameters pbParams = new PKIXBuilderParameters(truststore, certSelect);
 pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList)));

 // Set maximum certification path length
 pbParams.setMaxPathLength(-1);

 // Enable revocation checking
 pbParams.setRevocationEnabled(true);

 // Set static Certificate Revocation List
 if (_crls != null && !_crls.isEmpty())
     pbParams.addCertStore(CertStore.getInstance("Collection", new CollectionCertStoreParameters(_crls)));

  // Enable On-Line Certificate Status Protocol (OCSP) support
  if (_enableOCSP)
      Security.setProperty("ocsp.enable","true");

  // Enable Certificate Revocation List Distribution Points (CRLDP) support
  if (_enableCRLDP)
      System.setProperty("com.sun.security.enableCRLDP","true");

 // Build certification path
 CertPathBuilderResult buildResult = CertPathBuilder.getInstance("PKIX").build(pbParams);               

 // Validate certification path
 CertPathValidator.getInstance("PKIX").validate(buildResult.getCertPath(),pbParams);
}catch(GeneralSecurityException gse){
 ...
}

Конечно, я должен использовать второй способ ... Итак, давайте сконцентрируемся на этом коде, это хороший способ проверить подписанный сертификат? Вот дамп моих хранилищ ключей:

Клиентское хранилище ключей:

Entry type: PrivateKeyEntry 
Certificate chain length: 2
Certificate[1]: 
Owner: [email protected], CN=Servlet, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR 
Issuer: [email protected], CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR 
...

Certificate[2]: 
Owner: [email protected], CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: [email protected], CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR 
...

Сервер доверенных сертификатов:

Entry type: trustedCertEntry

Owner: [email protected], CN=Greenpacs, OU=dev, O=Imbasoft, L=Bondy, ST=Ile-de-France, C=FR
Issuer: [email protected], CN=Greenpacs Certificate Authority, OU=dev, O=Imbasoft, ST=Ile-de-France, C=FR

Я не уверен в этих хранилищах ключей, но я пробовал с другим (добавление сертификата CA в цепочку сертификатов клиента, добавление сертификата в хранилище доверенных сертификатов), но проверка все еще не выполняется. И с этими хранилищами ключей, похоже, работает первый способ проверки (SSLEngine).

Вывод отладки слишком велик, чтобы помещать его сюда, но вот трассировка стека:

java.security.cert.CertPathValidatorException: Could not determine revocation status
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:153)
    at sun.security.provider.certpath.PKIXCertPathValidator.doValidate(PKIXCertPathValidator.java:325)
    at sun.security.provider.certpath.PKIXCertPathValidator.engineValidate(PKIXCertPathValidator.java:187)
    at java.security.cert.CertPathValidator.validate(CertPathValidator.java:267)
    at MainClass.main(MainClass.java:75)
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:197)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:255)
    at sun.security.provider.certpath.CrlRevocationChecker.buildToNewKey(CrlRevocationChecker.java:583)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyWithSeparateSigningKey(CrlRevocationChecker.java:459)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyRevocationStatus(CrlRevocationChecker.java:339)
    at sun.security.provider.certpath.CrlRevocationChecker.verifyRevocationStatus(CrlRevocationChecker.java:248)
    at sun.security.provider.certpath.CrlRevocationChecker.check(CrlRevocationChecker.java:189)
    at sun.security.provider.certpath.PKIXMasterCertPathValidator.validate(PKIXMasterCertPathValidator.java:131)
    ... 4 more

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

Я начинаю сомневаться в коде причала, но я не эксперт в сертификатах и ​​подтверждении связи SSL, поэтому он также может исходить из плохого хранилища ключей / доверенного хранилища. Вот почему я не создавал проблему на доске причала и не спрашивал здесь раньше, чтобы убедиться, что код нужно изменить.

Также может быть полезно узнать, как проверить подписанный сертификат на Java.


person Ghetolay    schedule 17.09.2012    source источник
comment
Почему бы вам не позволить доверительному менеджеру через JSSE сделать все это за вас?   -  person Bruno    schedule 17.09.2012
comment
Потому что мой сервер также принимает соединение без сертификата. Я должен сначала позволить ему принимать все соединения, а затем, в некоторых случаях, проверять сертификат клиента, если таковой имеется.   -  person Ghetolay    schedule 17.09.2012
comment
Нет, вам просто нужно сделать сертификат клиента необязательным с помощью параметра wantClientAuth (вместо необходимости).   -  person Bruno    schedule 17.09.2012
comment
На самом деле это так, но я думал, что он не проверяет сертификат клиента. Я провел несколько тестов после прочтения вашего комментария, и, похоже, он работает следующим образом: если клиент отправляет действительный сертификат клиента, соединение принимается, а атрибут запроса X509Certificate устанавливается с действующим сертификатом. Если недействительный сертификат или сертификат не отправлен, соединение все равно принимается, но атрибут запроса не установлен. Правильно ли это, это означает, что мне просто нужно проверить атрибут запроса, чтобы узнать, действителен ли сертификат. Вы можете подтвердить такое поведение?   -  person Ghetolay    schedule 17.09.2012
comment
Да, именно так это и должно работать, когда вы настраиваете аутентификацию клиент-сертификат как необязательную. Я думаю, что соединение будет отклонено, если сертификату не доверяют (то есть клиент не должен отправлять сертификат вообще, если его сертификат не доверяет).   -  person Bruno    schedule 17.09.2012
comment
@Bruno Это верно. «needClientAuth» на самом деле вообще не является частью RFC 2246, это Java-ism и, возможно, также OpenSSL-ism. Идея состоит в том, что если действительный сертификат не предоставлен, сервер закрывает соединение, но он не указан должным образом нигде, о чем я знаю, и я искал около 9 лет. Если бы это было частью RFC 2246, можно было бы ожидать генерации какого-либо предупреждения.   -  person user207421    schedule 18.09.2012
comment
Как вы получили сертификат сервера для подписи сертификата клиента? Центры сертификации не выдадут вам сертификат подписи. Также почему?   -  person user207421    schedule 06.05.2020


Ответы (2)


На самом деле мне не нужно проводить валидацию самостоятельно.

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

Используя Jetty (или любой Java ServletContainer, я полагаю), вам просто нужно проверить атрибут HttpServletRequest [javax.servlet.request.X509Certificate], чтобы узнать, был ли отправлен действительный сертификат клиентом.

Я до сих пор не знаю, как проверить сертификат на Java, но мне этого решения достаточно :) Мне больше не нужно делать это самому. Спасибо Бруно!

person Ghetolay    schedule 17.09.2012
comment
getPeerCertificateChain () не возвращает null, а выдает исключение. - person user207421; 18.09.2012
comment
Если отправляется недействительный сертификат, рукопожатие вызывает исключение задолго до того, как вы сможете вызвать getPeerCertificateChain(). - person user207421; 03.05.2021

Проверьте, доступен ли CRL вашего сертификата или OCSP, вы можете найти такую ​​информацию в сертификате, например

[1]CRL Distribution Point
     Distribution Point Name:
          Full Name:
               URL=http://crl.verisign.com/pca2-g2.crl
person Ted Shaw    schedule 17.09.2012
comment
Я не использую CRL, OSCP или CRLDP. В моем сертификате такой строчки нет. Также показанный код не является полным, есть некоторые, если для CRL / OSCP / CRLDP, но они никогда не достигаются, потому что я не устанавливал эти параметры. Нужно ли мне ? это абсолютно необходимо? - person Ghetolay; 17.09.2012