Last active
February 25, 2021 10:24
-
-
Save as1an/8533a5c3f9ae9787de50a44457b242b8 to your computer and use it in GitHub Desktop.
Построение иерархии сертификатов НУЦ\КУЦ и проверка на действительность по CRL или OCSP
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package kz.gov.pki.provider.utils; | |
import java.security.InvalidAlgorithmParameterException; | |
import java.security.NoSuchAlgorithmException; | |
import java.security.NoSuchProviderException; | |
import java.security.cert.CertPathBuilder; | |
import java.security.cert.CertPathBuilderException; | |
import java.security.cert.CertPathBuilderResult; | |
import java.security.cert.CertStore; | |
import java.security.cert.CollectionCertStoreParameters; | |
import java.security.cert.TrustAnchor; | |
import java.security.cert.X509CRL; | |
import java.security.cert.X509Certificate; | |
import java.security.cert.X509Extension; | |
import java.util.ArrayList; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.Date; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.LinkedList; | |
import java.util.Map; | |
import java.util.Set; | |
import javax.security.auth.x500.X500Principal; | |
import kz.gov.pki.kalkan.jce.provider.KalkanProvider; | |
import kz.gov.pki.kalkan.pkix.checker.KNCAOCSPChecker; | |
import kz.gov.pki.kalkan.x509.ExtendedPKIXBuilderParameters; | |
import kz.gov.pki.kalkan.x509.X509CertStoreSelector; | |
import kz.gov.pki.provider.exception.ProviderUtilException; | |
import kz.gov.pki.provider.exception.ProviderUtilExceptionCode; | |
/** | |
* Класс валидации сертификата по цепочке | |
* | |
*/ | |
public class PKIXUtil { | |
private Map<X500Principal, X509Certificate> caCertsMap = new HashMap<X500Principal, X509Certificate>(); | |
private KNCAOCSPChecker ocspChecker; | |
private X509Certificate targetCert; | |
private Collection<X509Certificate> caCertList; | |
private Collection<X509CRL> crlList; | |
private Date checkDate; | |
private boolean allowExpired = false; | |
/** | |
* Конструктор | |
* @param targetCert конечный сертификат | |
* @param caCertList Список сертификатов УЦ сертификата для верификации цепочки. Можно до КУЦ или НУЦ | |
*/ | |
public PKIXUtil(X509Certificate targetCert, Collection<X509Certificate> caCertList) { | |
this.targetCert = targetCert; | |
this.caCertList = caCertList == null ? new ArrayList<X509Certificate>() : caCertList; | |
for (X509Certificate cert : caCertList) { | |
caCertsMap.put(cert.getSubjectX500Principal(), cert); | |
} | |
} | |
/** | |
* Добавить дату проверки. | |
* @param checkDate дата проверки | |
* @return возвращает текущий объект | |
*/ | |
public PKIXUtil withDate(Date checkDate) { | |
this.checkDate = checkDate; | |
return this; | |
} | |
/** | |
* Добавить проверку по OCSP | |
* @return возвращает текущий объект | |
*/ | |
public PKIXUtil withOCSP() { | |
ocspChecker = new KNCAOCSPChecker(this.caCertsMap); | |
return this; | |
} | |
/** | |
* Добавить проверку по CRL. Если добавлен {@link PKIXUtil#withDate(Date)}, то список должен содержать | |
* актуальные именно на тот момент CRL. | |
* @param crlList Список актуальных полных и дельта (опционально) CRL для всех указанных сертификатов | |
* в caCertList. | |
* @return возвращает текущий объект | |
*/ | |
public PKIXUtil withCRL(Collection<X509CRL> crlList) { | |
this.crlList = crlList == null ? new ArrayList<X509CRL>() : crlList; | |
return this; | |
} | |
/** | |
* Разрешить проверять просроченные сертификаты. Не имеет эффекта при добавлении | |
* {@link PKIXUtil#withDate(Date)} | |
* Дата проверки установится на {@link X509Certificate#getNotAfter()} проверяемого сертификата. | |
* @return возвращает текущий объект | |
*/ | |
public PKIXUtil allowExpired() { | |
allowExpired = true; | |
return this; | |
} | |
/** | |
* Проверка сертификата с построением цепочки | |
* @return возвращает цепочку без Trusted Anchor | |
* @throws InvalidAlgorithmParameterException | |
* @throws NoSuchAlgorithmException | |
* @throws NoSuchProviderException | |
* @throws CertPathBuilderException | |
* @throws ProviderUtilException | |
*/ | |
public CertPathBuilderResult validate() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, | |
NoSuchProviderException, CertPathBuilderException, ProviderUtilException { | |
if (caCertList.size() == 0) { | |
throw new ProviderUtilException(ProviderUtilExceptionCode.NO_CACERT_LIST); | |
} | |
LinkedList<X509Extension> chainList = new LinkedList<X509Extension>(); | |
chainList.add(targetCert); | |
X509Certificate trustedCert = caCertsMap.get(targetCert.getIssuerX500Principal()); | |
if (trustedCert == null) { | |
throw new ProviderUtilException(ProviderUtilExceptionCode.ISSUER_CERT_NOT_FOUND); | |
} | |
chainList.add(trustedCert); | |
for (int i = 0; i < caCertsMap.size(); i++) { | |
X509Certificate cert = ((X509Certificate) chainList.peekLast()); | |
if (cert.getIssuerX500Principal().equals(cert.getSubjectX500Principal())) { | |
break; | |
} | |
X509Certificate topTrustedCert = caCertsMap.get(cert.getIssuerX500Principal()); | |
if (topTrustedCert != null) { | |
chainList.add(topTrustedCert); | |
} else { | |
break; | |
} | |
} | |
Collections.reverse(chainList); | |
Set<TrustAnchor> trustedAnchors = new HashSet<TrustAnchor>(); | |
trustedAnchors.add(new TrustAnchor((X509Certificate) chainList.getFirst(), null)); | |
X509CertStoreSelector selector = new X509CertStoreSelector(); | |
selector.setCertificate(targetCert); | |
ExtendedPKIXBuilderParameters builderParams = new ExtendedPKIXBuilderParameters(trustedAnchors, selector); | |
builderParams.setSigProvider(KalkanProvider.PROVIDER_NAME); | |
builderParams.setUseDeltasEnabled(true); | |
if (allowExpired) { | |
if (targetCert.getNotAfter().before(new Date())) { | |
builderParams.setDate(targetCert.getNotAfter()); | |
} | |
} | |
if (checkDate != null) { | |
builderParams.setDate(checkDate); | |
} | |
if (crlList == null) { | |
builderParams.setRevocationEnabled(false); | |
} else if (crlList.size() == 0) { | |
throw new ProviderUtilException(ProviderUtilExceptionCode.NO_CRL_LIST); | |
} else { | |
Date validDate = checkDate == null ? new Date() : checkDate; | |
for (X509CRL crl : crlList) { | |
if (validDate.after(crl.getNextUpdate())) { | |
throw new ProviderUtilException(ProviderUtilExceptionCode.OUTDATED_CRL); | |
} | |
} | |
chainList.addAll(crlList); | |
} | |
CollectionCertStoreParameters certStoreParams = new CollectionCertStoreParameters(chainList); | |
CertStore certStore = CertStore.getInstance("Collection", certStoreParams, KalkanProvider.PROVIDER_NAME); | |
builderParams.addCertStore(certStore); | |
builderParams.addCertPathChecker(ocspChecker); | |
CertPathBuilder certPathBuilder = CertPathBuilder.getInstance("PKIX", KalkanProvider.PROVIDER_NAME); | |
return certPathBuilder.build(builderParams); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment