Skip to content

Instantly share code, notes, and snippets.

@as1an
Last active February 25, 2021 10:24
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save as1an/8533a5c3f9ae9787de50a44457b242b8 to your computer and use it in GitHub Desktop.
Save as1an/8533a5c3f9ae9787de50a44457b242b8 to your computer and use it in GitHub Desktop.
Построение иерархии сертификатов НУЦ\КУЦ и проверка на действительность по CRL или OCSP
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