eu.europa.esig.dss.spi.CertificateExtensionsUtils Maven / Gradle / Ivy
/**
* DSS - Digital Signature Services
* Copyright (C) 2015 European Commission, provided under the CEF programme
*
* This file is part of the "DSS - Digital Signature Services" project.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package eu.europa.esig.dss.spi;
import eu.europa.esig.dss.enumerations.CertificateExtensionEnum;
import eu.europa.esig.dss.enumerations.GeneralNameType;
import eu.europa.esig.dss.enumerations.KeyUsageBit;
import eu.europa.esig.dss.model.DSSException;
import eu.europa.esig.dss.model.x509.CertificateToken;
import eu.europa.esig.dss.model.x509.X500PrincipalHelper;
import eu.europa.esig.dss.model.x509.extension.AuthorityInformationAccess;
import eu.europa.esig.dss.model.x509.extension.AuthorityKeyIdentifier;
import eu.europa.esig.dss.model.x509.extension.BasicConstraints;
import eu.europa.esig.dss.model.x509.extension.CRLDistributionPoints;
import eu.europa.esig.dss.model.x509.extension.CertificateExtension;
import eu.europa.esig.dss.model.x509.extension.CertificateExtensions;
import eu.europa.esig.dss.model.x509.extension.CertificatePolicies;
import eu.europa.esig.dss.model.x509.extension.CertificatePolicy;
import eu.europa.esig.dss.model.x509.extension.ExtendedKeyUsages;
import eu.europa.esig.dss.model.x509.extension.FreshestCRL;
import eu.europa.esig.dss.model.x509.extension.GeneralName;
import eu.europa.esig.dss.model.x509.extension.GeneralSubtree;
import eu.europa.esig.dss.model.x509.extension.InhibitAnyPolicy;
import eu.europa.esig.dss.model.x509.extension.KeyUsage;
import eu.europa.esig.dss.model.x509.extension.NameConstraints;
import eu.europa.esig.dss.model.x509.extension.NoRevAvail;
import eu.europa.esig.dss.model.x509.extension.OCSPNoCheck;
import eu.europa.esig.dss.model.x509.extension.PolicyConstraints;
import eu.europa.esig.dss.model.x509.extension.QcStatements;
import eu.europa.esig.dss.model.x509.extension.SubjectAlternativeNames;
import eu.europa.esig.dss.model.x509.extension.SubjectKeyIdentifier;
import eu.europa.esig.dss.model.x509.extension.ValidityAssuredShortTerm;
import eu.europa.esig.dss.utils.Utils;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1OctetString;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.ocsp.OCSPObjectIdentifiers;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.RFC4519Style;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.CRLDistPoint;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.IssuerSerial;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.PolicyQualifierId;
import org.bouncycastle.asn1.x509.PolicyQualifierInfo;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.security.auth.x500.X500Principal;
import java.io.IOException;
import java.math.BigInteger;
import java.security.cert.CertificateParsingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* This class contains utility methods for extraction certificate extension (whether critical or not)
*
*/
public class CertificateExtensionsUtils {
private static final Logger LOG = LoggerFactory.getLogger(CertificateExtensionsUtils.class);
/**
* Utils class
*/
private CertificateExtensionsUtils() {
// empty
}
/**
* This method extracts the certificate extensions from the given {@code certificateToken}
*
* @param certificateToken {@link CertificateToken} to get certificate extension from
* @return {@link CertificateExtensions}
*/
public static CertificateExtensions getCertificateExtensions(CertificateToken certificateToken) {
final CertificateExtensions certificateExtensions = new CertificateExtensions();
setCertificateExtensions(certificateExtensions, certificateToken, certificateToken.getCertificate().getCriticalExtensionOIDs());
setCertificateExtensions(certificateExtensions, certificateToken, certificateToken.getCertificate().getNonCriticalExtensionOIDs());
return certificateExtensions;
}
private static void setCertificateExtensions(CertificateExtensions certificateExtensions,
CertificateToken certificateToken, Collection extensionOIDs) {
if (Utils.isCollectionNotEmpty(extensionOIDs)) {
for (String oid : extensionOIDs) {
if (isSubjectAlternativeNames(oid)) {
certificateExtensions.setSubjectAlternativeNames(getSubjectAlternativeNames(certificateToken));
} else if (isAuthorityKeyIdentifier(oid)) {
certificateExtensions.setAuthorityKeyIdentifier(getAuthorityKeyIdentifier(certificateToken));
} else if (isSubjectKeyIdentifier(oid)) {
certificateExtensions.setSubjectKeyIdentifier(getSubjectKeyIdentifier(certificateToken));
} else if (isAuthorityInformationAccess(oid)) {
certificateExtensions.setAuthorityInformationAccess(getAuthorityInformationAccess(certificateToken));
} else if (isCRLDistributionPoints(oid)) {
certificateExtensions.setCRLDistributionPoints(getCRLDistributionPoints(certificateToken));
} else if (isBasicConstraints(oid)) {
certificateExtensions.setBasicConstraints(getBasicConstraints(certificateToken));
} else if (isNameConstraints(oid)) {
certificateExtensions.setNameConstraints(getNameConstraints(certificateToken));
} else if (isPolicyConstraints(oid)) {
certificateExtensions.setPolicyConstraints(getPolicyConstraints(certificateToken));
} else if (isInhibitAnyPolicy(oid)) {
certificateExtensions.setInhibitAnyPolicy(getInhibitAnyPolicy(certificateToken));
} else if (isFreshestCRL(oid)) {
certificateExtensions.setFreshestCRL(getFreshestCRL(certificateToken));
} else if (isKeyUsage(oid)) {
certificateExtensions.setKeyUsage(getKeyUsage(certificateToken));
} else if (isExtendedKeyUsage(oid)) {
certificateExtensions.setExtendedKeyUsage(getExtendedKeyUsage(certificateToken));
} else if (isCertificatePolicies(oid)) {
certificateExtensions.setCertificatePolicies(getCertificatePolicies(certificateToken));
} else if (isOcspNoCheck(oid)) {
certificateExtensions.setOcspNoCheck(getOcspNoCheck(certificateToken));
} else if (isValidityAssuredShortTerm(oid)) {
certificateExtensions.setValidityAssuredShortTerm(getValAssuredSTCerts(certificateToken));
} else if (isQcStatements(oid)) {
certificateExtensions.setQcStatements(getQcStatements(certificateToken));
} else if (isNoRevocationAvailable(oid)) {
certificateExtensions.setNoRevAvail(getNoRevAvail(certificateToken));
} else {
certificateExtensions.addOtherExtension(getOtherCertificateExtension(certificateToken, oid));
}
}
}
}
/**
* This method verifies whether {@code oid} corresponds to the subject alternative names extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the subject alternative names extension OID, FALSE otherwise
*/
public static boolean isSubjectAlternativeNames(String oid) {
return CertificateExtensionEnum.SUBJECT_ALTERNATIVE_NAME.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the authority key identifier extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the authority key identifier extension OID, FALSE otherwise
*/
public static boolean isAuthorityKeyIdentifier(String oid) {
return CertificateExtensionEnum.AUTHORITY_KEY_IDENTIFIER.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the subject key identifier extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the subject key identifier extension OID, FALSE otherwise
*/
public static boolean isSubjectKeyIdentifier(String oid) {
return CertificateExtensionEnum.SUBJECT_KEY_IDENTIFIER.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the authority information access extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the authority information access extension OID, FALSE otherwise
*/
public static boolean isAuthorityInformationAccess(String oid) {
return CertificateExtensionEnum.AUTHORITY_INFORMATION_ACCESS.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the CRL distribution points extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the CRL distribution points extension OID, FALSE otherwise
*/
public static boolean isCRLDistributionPoints(String oid) {
return CertificateExtensionEnum.CRL_DISTRIBUTION_POINTS.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the basic constraints extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the basic constraints extension OID, FALSE otherwise
*/
public static boolean isBasicConstraints(String oid) {
return CertificateExtensionEnum.BASIC_CONSTRAINTS.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the name constraints extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the name constraints extension OID, FALSE otherwise
*/
public static boolean isNameConstraints(String oid) {
return CertificateExtensionEnum.NAME_CONSTRAINTS.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the policy constraints extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the policy constraints extension OID, FALSE otherwise
*/
public static boolean isPolicyConstraints(String oid) {
return CertificateExtensionEnum.POLICY_CONSTRAINTS.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the key usage extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the key usage extension OID, FALSE otherwise
*/
public static boolean isKeyUsage(String oid) {
return CertificateExtensionEnum.KEY_USAGE.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the extended key usage extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the extended key usage extension OID, FALSE otherwise
*/
public static boolean isExtendedKeyUsage(String oid) {
return CertificateExtensionEnum.EXTENDED_KEY_USAGE.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the policy constraints extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the policy constraints extension OID, FALSE otherwise
*/
public static boolean isInhibitAnyPolicy(String oid) {
return CertificateExtensionEnum.INHIBIT_ANY_POLICY.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the Freshest CRL (a.k.a. Delta CRL) extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the Freshest CRL extension OID, FALSE otherwise
*/
public static boolean isFreshestCRL(String oid) {
return CertificateExtensionEnum.FRESHEST_CRL.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the certificate policies extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the certificate policies extension OID, FALSE otherwise
*/
public static boolean isCertificatePolicies(String oid) {
return CertificateExtensionEnum.CERTIFICATE_POLICIES.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the ocsp-nocheck extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the ocsp-nocheck extension OID, FALSE otherwise
*/
public static boolean isOcspNoCheck(String oid) {
return CertificateExtensionEnum.OCSP_NOCHECK.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the ext-etsi-valassured-ST-certs extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the ext-etsi-valassured-ST-certs extension OID, FALSE otherwise
*/
public static boolean isValidityAssuredShortTerm(String oid) {
return CertificateExtensionEnum.VALIDITY_ASSURED_SHORT_TERM.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the qc-statements extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the qc-statements extension OID, FALSE otherwise
*/
public static boolean isQcStatements(String oid) {
return CertificateExtensionEnum.QC_STATEMENTS.getOid().equals(oid);
}
/**
* This method verifies whether {@code oid} corresponds to the noRevAvail extension OID
*
* @param oid {@link String}
* @return TRUE if OID corresponds to the noRevAvail extension OID, FALSE otherwise
*/
public static boolean isNoRevocationAvailable(String oid) {
return CertificateExtensionEnum.NO_REVOCATION_AVAILABLE.getOid().equals(oid);
}
/**
* Returns a subject alternative names, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link SubjectAlternativeNames}
*/
public static SubjectAlternativeNames getSubjectAlternativeNames(CertificateToken certificateToken) {
try {
final SubjectAlternativeNames subjectAlternateNames = new SubjectAlternativeNames();
subjectAlternateNames.setOctets(certificateToken.getCertificate().getExtensionValue(subjectAlternateNames.getOid()));
final List result = new ArrayList<>();
Collection> subjectAlternativeNames = certificateToken.getCertificate().getSubjectAlternativeNames();
if (Utils.isCollectionNotEmpty(subjectAlternativeNames)) {
for (List> gn : subjectAlternativeNames) {
GeneralName generalName = getGeneralName(gn);
if (generalName != null) {
result.add(generalName);
}
}
}
subjectAlternateNames.setGeneralNames(result);
subjectAlternateNames.checkCritical(certificateToken);
return subjectAlternateNames;
} catch (Exception e) {
LOG.warn("Unable to extract SubjectAlternativeNames", e);
return null;
}
}
private static GeneralName getGeneralName(List> gn) {
if (Utils.collectionSize(gn) == 2) {
try {
if (!(gn.get(0) instanceof Integer)) {
LOG.warn("Unable to load the alternative name. Reason : Invalid encoding!");
return null;
}
final GeneralName generalName = new GeneralName();
GeneralNameType type = GeneralNameType.fromIndex((Integer) gn.get(0));
generalName.setGeneralNameType(type);
Object value = gn.get(1);
if (value instanceof String) {
String strValue = (String) value;
if (GeneralNameType.DIRECTORY_NAME.equals(type)) {
strValue = toRFC2253RDN(strValue);
}
generalName.setValue(strValue);
} else if (value instanceof byte[]) {
generalName.setValue(toHexEncoded((byte[]) value));
} else {
LOG.warn("Unable to load the alternative name. Reason : Unsupported value type!");
return null;
}
return generalName;
} catch (Exception e) {
LOG.warn("Unable to load the alternative name. Reason : {}", e.getMessage(), e);
}
} else {
LOG.warn("Unable to load the alternative name. Reason : Invalid sequence length!");
}
return null;
}
private static String toRFC2253RDN(String value) {
try {
// RFC4519Style is the default style used in BouncyCastle
RDN[] rdns = RFC4519Style.INSTANCE.fromString(value);
X500Principal x500Principal = new X500Principal(DSSASN1Utils.getDEREncoded(new X500Name(rdns)));
return new X500PrincipalHelper(x500Principal).getRFC2253();
} catch (Exception e) {
LOG.warn("Unable to build RDN! Reason: {}", e.getMessage(), e);
return value;
}
}
/**
* Returns the authority information access, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link AuthorityInformationAccess}
*/
public static AuthorityInformationAccess getAuthorityInformationAccess(CertificateToken certificateToken) {
final byte[] authInfoAccessExtensionValue = certificateToken.getCertificate()
.getExtensionValue(CertificateExtensionEnum.AUTHORITY_INFORMATION_ACCESS.getOid());
if (Utils.isArrayEmpty(authInfoAccessExtensionValue)) {
return null;
}
try {
ASN1Sequence asn1Sequence = DSSASN1Utils.getAsn1SequenceFromDerOctetString(authInfoAccessExtensionValue);
if (asn1Sequence == null || asn1Sequence.size() == 0) {
LOG.warn("Empty ASN1Sequence for AuthorityInformationAccess");
return null;
}
final AuthorityInformationAccess authorityInformationAccess = new AuthorityInformationAccess();
authorityInformationAccess.setOctets(authInfoAccessExtensionValue);
org.bouncycastle.asn1.x509.AuthorityInformationAccess aia = org.bouncycastle.asn1.x509.AuthorityInformationAccess.getInstance(asn1Sequence);
AccessDescription[] accessDescriptions = aia.getAccessDescriptions();
authorityInformationAccess.setCaIssuers(getAccessUrls(accessDescriptions, X509ObjectIdentifiers.id_ad_caIssuers));
authorityInformationAccess.setOcsp(getAccessUrls(accessDescriptions, X509ObjectIdentifiers.id_ad_ocsp));
authorityInformationAccess.checkCritical(certificateToken);
return authorityInformationAccess;
} catch (Exception e) {
LOG.warn("Unable to parse authorityInfoAccess", e);
return null;
}
}
private static List getAccessUrls(AccessDescription[] accessDescriptions, ASN1ObjectIdentifier aiaOid) {
final List locationsUrls = new ArrayList<>();
for (AccessDescription accessDescription : accessDescriptions) {
if (aiaOid.equals(accessDescription.getAccessMethod())) {
org.bouncycastle.asn1.x509.GeneralName gn = accessDescription.getAccessLocation();
String location = parseGn(gn);
if (location != null) {
locationsUrls.add(location);
}
}
}
return locationsUrls;
}
/**
* Returns the CA issuers URIs extracted from authorityInfoAccess.caIssuers field
*
* @param certificate {@link CertificateToken}
* @return a list of CA issuers URIs, or empty list if the extension is not present.
*/
public static List getCAIssuersAccessUrls(final CertificateToken certificate) {
AuthorityInformationAccess aia = CertificateExtensionsUtils.getAuthorityInformationAccess(certificate);
return aia != null ? aia.getCaIssuers() : Collections.emptyList();
}
/**
* Returns the OCSP URIs extracted from authorityInfoAccess.ocsp field
*
* @param certificate {@link CertificateToken}
* @return a list of OCSP URIs, or empty list if the extension is not present.
*/
public static List getOCSPAccessUrls(final CertificateToken certificate) {
AuthorityInformationAccess aia = CertificateExtensionsUtils.getAuthorityInformationAccess(certificate);
return aia != null ? aia.getOcsp() : Collections.emptyList();
}
/**
* Returns the subject key identifier, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link SubjectKeyIdentifier}
*/
public static AuthorityKeyIdentifier getAuthorityKeyIdentifier(CertificateToken certificateToken) {
byte[] extensionValue = certificateToken.getCertificate().getExtensionValue(CertificateExtensionEnum.AUTHORITY_KEY_IDENTIFIER.getOid());
if (Utils.isArrayEmpty(extensionValue)) {
return null;
}
try {
final AuthorityKeyIdentifier authorityKeyIdentifier = new AuthorityKeyIdentifier();
authorityKeyIdentifier.setOctets(extensionValue);
ASN1Primitive extension = JcaX509ExtensionUtils.parseExtensionValue(extensionValue);
org.bouncycastle.asn1.x509.AuthorityKeyIdentifier aki = org.bouncycastle.asn1.x509.AuthorityKeyIdentifier.getInstance(extension);
authorityKeyIdentifier.setKeyIdentifier(aki.getKeyIdentifier());
if (aki.getAuthorityCertIssuer() != null && aki.getAuthorityCertSerialNumber() != null) {
IssuerSerial issuerSerial = new IssuerSerial(aki.getAuthorityCertIssuer(), aki.getAuthorityCertSerialNumber());
authorityKeyIdentifier.setAuthorityCertIssuerSerial(DSSASN1Utils.getDEREncoded(issuerSerial));
}
authorityKeyIdentifier.checkCritical(certificateToken);
return authorityKeyIdentifier;
} catch (IOException e) {
throw new DSSException(String.format("Unable to retrieve authority key identifier of a certificate. " +
"Reason : %s", e.getMessage()), e);
}
}
/**
* Returns the subject key identifier, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link SubjectKeyIdentifier}
*/
public static SubjectKeyIdentifier getSubjectKeyIdentifier(CertificateToken certificateToken) {
byte[] extensionValue = certificateToken.getCertificate().getExtensionValue(CertificateExtensionEnum.SUBJECT_KEY_IDENTIFIER.getOid());
if (Utils.isArrayEmpty(extensionValue)) {
return null;
}
try {
final SubjectKeyIdentifier subjectKeyIdentifier = new SubjectKeyIdentifier();
subjectKeyIdentifier.setOctets(extensionValue);
ASN1Primitive extension = JcaX509ExtensionUtils.parseExtensionValue(extensionValue);
org.bouncycastle.asn1.x509.SubjectKeyIdentifier skiBC = org.bouncycastle.asn1.x509.SubjectKeyIdentifier.getInstance(extension);
subjectKeyIdentifier.setSki(skiBC.getKeyIdentifier());
subjectKeyIdentifier.checkCritical(certificateToken);
return subjectKeyIdentifier;
} catch (IOException e) {
throw new DSSException(String.format("Unable to retrieve subject key identifier of a certificate. " +
"Reason : %s", e.getMessage()), e);
}
}
/**
* Returns the CRL distribution points, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link CRLDistributionPoints}
*/
public static CRLDistributionPoints getCRLDistributionPoints(CertificateToken certificateToken) {
final byte[] crlDistributionPointsBytes = certificateToken.getCertificate().getExtensionValue(CertificateExtensionEnum.CRL_DISTRIBUTION_POINTS.getOid());
if (crlDistributionPointsBytes != null) {
final CRLDistributionPoints crlDistributionPoints = new CRLDistributionPoints();
crlDistributionPoints.setOctets(crlDistributionPointsBytes);
crlDistributionPoints.setCrlUrls(getCRLDistributionPointsUrls(crlDistributionPointsBytes));
crlDistributionPoints.checkCritical(certificateToken);
return crlDistributionPoints;
}
return null;
}
private static List getCRLDistributionPointsUrls(byte[] crlDistributionPointsBytes) {
if (crlDistributionPointsBytes != null) {
try {
final List urls = new ArrayList<>();
final ASN1Sequence asn1Sequence = DSSASN1Utils.getAsn1SequenceFromDerOctetString(crlDistributionPointsBytes);
final CRLDistPoint distPoint = CRLDistPoint.getInstance(asn1Sequence);
final DistributionPoint[] distributionPoints = distPoint.getDistributionPoints();
for (final DistributionPoint distributionPoint : distributionPoints) {
final DistributionPointName distributionPointName = distributionPoint.getDistributionPoint();
if (DistributionPointName.FULL_NAME != distributionPointName.getType()) {
continue;
}
final GeneralNames generalNames = (GeneralNames) distributionPointName.getName();
final org.bouncycastle.asn1.x509.GeneralName[] names = generalNames.getNames();
for (final org.bouncycastle.asn1.x509.GeneralName name : names) {
String location = parseGn(name);
if (location != null) {
urls.add(location);
}
}
}
return urls;
} catch (Exception e) {
LOG.warn("Unable to parse cRLDistributionPoints", e);
}
}
return Collections.emptyList();
}
private static String parseGn(org.bouncycastle.asn1.x509.GeneralName gn) {
try {
if (org.bouncycastle.asn1.x509.GeneralName.uniformResourceIdentifier == gn.getTagNo()) {
ASN1String str = (ASN1String) ((DERTaggedObject) gn.toASN1Primitive()).getBaseObject();
return str.getString();
}
} catch (Exception e) {
LOG.warn("Unable to parse GN '{}'", gn, e);
}
return null;
}
/**
* Returns the CRL distribution URIs extracted from cRLDistributionPoints field
*
* @param certificate {@link CertificateToken}
* @return a list of CA issuers URIs, or empty list if the extension is not present.
*/
public static List getCRLAccessUrls(final CertificateToken certificate) {
CRLDistributionPoints crlDistributionPoints = CertificateExtensionsUtils.getCRLDistributionPoints(certificate);
return crlDistributionPoints != null ? crlDistributionPoints.getCrlUrls() : Collections.emptyList();
}
/**
* Returns a basic constraints extension, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link BasicConstraints}
*/
public static BasicConstraints getBasicConstraints(CertificateToken certificateToken) {
final BasicConstraints basicConstraints = new BasicConstraints();
basicConstraints.setOctets(certificateToken.getCertificate().getExtensionValue(basicConstraints.getOid()));
final int value = certificateToken.getCertificate().getBasicConstraints();
basicConstraints.setCa(value != -1);
basicConstraints.setPathLenConstraint(value);
basicConstraints.checkCritical(certificateToken);
return basicConstraints;
}
/**
* Returns a name constraints extension, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link NameConstraints}
*/
public static NameConstraints getNameConstraints(CertificateToken certificateToken) {
final byte[] nameConstraintsBinaries = certificateToken.getCertificate()
.getExtensionValue(CertificateExtensionEnum.NAME_CONSTRAINTS.getOid());
if (Utils.isArrayNotEmpty(nameConstraintsBinaries)) {
try {
ASN1Sequence seq = DSSASN1Utils.getAsn1SequenceFromDerOctetString(nameConstraintsBinaries);
org.bouncycastle.asn1.x509.NameConstraints bcNameConstraints = org.bouncycastle.asn1.x509.NameConstraints.getInstance(seq);
final NameConstraints nameConstraints = new NameConstraints();
nameConstraints.setOctets(nameConstraintsBinaries);
nameConstraints.setPermittedSubtrees(getGeneralSubtrees(bcNameConstraints.getPermittedSubtrees()));
nameConstraints.setExcludedSubtrees(getGeneralSubtrees(bcNameConstraints.getExcludedSubtrees()));
nameConstraints.checkCritical(certificateToken);
return nameConstraints;
} catch (Exception e) {
LOG.warn("Unable to parse the nameConstraints extension '{}' : {}",
Utils.toBase64(nameConstraintsBinaries), e.getMessage(), e);
}
}
return null;
}
private static List getGeneralSubtrees(org.bouncycastle.asn1.x509.GeneralSubtree[] generalSubtrees) {
if (Utils.isArrayEmpty(generalSubtrees)) {
return Collections.emptyList();
}
final List result = new ArrayList<>();
for (org.bouncycastle.asn1.x509.GeneralSubtree bcGeneralSubtree : generalSubtrees) {
final GeneralSubtree generalSubtree = new GeneralSubtree();
org.bouncycastle.asn1.x509.GeneralName generalName = bcGeneralSubtree.getBase();
GeneralNameType generalNameType = GeneralNameType.fromIndex(generalName.getTagNo());
if (generalNameType == null) {
LOG.warn("Unsupported GeneralName type of index '{}'!", generalName.getTagNo());
continue;
}
generalSubtree.setGeneralNameType(generalNameType);
generalSubtree.setMinimum(bcGeneralSubtree.getMinimum());
generalSubtree.setMaximum(bcGeneralSubtree.getMaximum());
generalSubtree.setValue(getStringValue(generalNameType, generalName.getName()));
result.add(generalSubtree);
}
return result;
}
private static String getStringValue(GeneralNameType generalNameType, ASN1Encodable generalNameValue) {
try {
switch (generalNameType) {
case OTHER_NAME:
case EDI_PARTY_NAME:
case X400_ADDRESS:
return toHexEncoded(generalNameValue);
case RFC822_NAME:
case DNS_NAME:
case UNIFORM_RESOURCE_IDENTIFIER:
if (generalNameValue instanceof ASN1String) {
return ((ASN1String) generalNameValue).getString();
} else {
LOG.warn("String value is expected for a General Name of type '{}'. " +
"Hex-encoded value is returned.", generalNameType);
return toHexEncoded(generalNameValue);
}
case DIRECTORY_NAME:
X500Principal x500Principal = new X500Principal(DSSASN1Utils.getDEREncoded(generalNameValue));
return new X500PrincipalHelper(x500Principal).getRFC2253();
case IP_ADDRESS:
byte[] octets = ASN1OctetString.getInstance(generalNameValue).getOctets();
return toHexEncoded(octets);
case REGISTERED_ID:
return ASN1ObjectIdentifier.getInstance(generalNameValue).getId();
default:
LOG.warn("Unsupported General Name of type '{}'. " +
"Hex-encoded value is returned.", generalNameType);
return toHexEncoded(generalNameValue);
}
} catch (Exception e) {
LOG.warn("An error occurred on extraction of General Name of Type '{}' : {}. " +
"Hex-encoded value is returned.", generalNameType, e.getMessage());
return toHexEncoded(generalNameValue);
}
}
private static String toHexEncoded(ASN1Encodable asn1Encodable) {
return toHexEncoded(DSSASN1Utils.getDEREncoded(asn1Encodable));
}
private static String toHexEncoded(byte[] binaries) {
return "#" + Utils.toHex(binaries);
}
/**
* Returns a policy constraints extension, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link PolicyConstraints}
*/
public static PolicyConstraints getPolicyConstraints(CertificateToken certificateToken) {
final byte[] policyConstraintsBinaries = certificateToken.getCertificate()
.getExtensionValue(CertificateExtensionEnum.POLICY_CONSTRAINTS.getOid());
if (Utils.isArrayNotEmpty(policyConstraintsBinaries)) {
final PolicyConstraints policyConstraints = new PolicyConstraints();
policyConstraints.setOctets(policyConstraintsBinaries);
try {
ASN1Sequence seq = DSSASN1Utils.getAsn1SequenceFromDerOctetString(policyConstraintsBinaries);
org.bouncycastle.asn1.x509.PolicyConstraints bcPolicyConstraints =
org.bouncycastle.asn1.x509.PolicyConstraints.getInstance(seq);
BigInteger inhibitPolicyMapping = bcPolicyConstraints.getInhibitPolicyMapping();
if (inhibitPolicyMapping != null) {
policyConstraints.setInhibitPolicyMapping(inhibitPolicyMapping.intValue());
}
BigInteger requireExplicitPolicyMapping = bcPolicyConstraints.getRequireExplicitPolicyMapping();
if (requireExplicitPolicyMapping != null) {
policyConstraints.setRequireExplicitPolicy(requireExplicitPolicyMapping.intValue());
}
policyConstraints.checkCritical(certificateToken);
return policyConstraints;
} catch (Exception e) {
LOG.warn("Unable to parse the policyConstraints extension '{}' : {}",
Utils.toBase64(policyConstraintsBinaries), e.getMessage(), e);
}
}
return null;
}
/**
* Returns an inhibit anyPolicy extension, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link PolicyConstraints}
*/
public static InhibitAnyPolicy getInhibitAnyPolicy(CertificateToken certificateToken) {
final byte[] inhibitAnyPolicyBinaries = certificateToken.getCertificate()
.getExtensionValue(CertificateExtensionEnum.INHIBIT_ANY_POLICY.getOid());
if (Utils.isArrayNotEmpty(inhibitAnyPolicyBinaries)) {
final InhibitAnyPolicy inhibitAnyPolicy = new InhibitAnyPolicy();
inhibitAnyPolicy.setOctets(inhibitAnyPolicyBinaries);
try {
ASN1Integer asn1Integer = DSSASN1Utils.getAsn1IntegerFromDerOctetString(inhibitAnyPolicyBinaries);
if (asn1Integer != null) {
BigInteger value = asn1Integer.getValue();
if (value != null) {
inhibitAnyPolicy.setValue(value.intValue());
inhibitAnyPolicy.checkCritical(certificateToken);
return inhibitAnyPolicy;
}
}
} catch (Exception e) {
LOG.warn("Unable to parse the inhibitAnyPolicy extension '{}' : {}",
Utils.toBase64(inhibitAnyPolicyBinaries), e.getMessage(), e);
}
}
return null;
}
/**
* Returns the Freshest CRL, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link FreshestCRL}
*/
public static FreshestCRL getFreshestCRL(CertificateToken certificateToken) {
final byte[] freshestCrlBytes = certificateToken.getCertificate().getExtensionValue(CertificateExtensionEnum.FRESHEST_CRL.getOid());
if (freshestCrlBytes != null) {
final FreshestCRL freshestCRL = new FreshestCRL();
freshestCRL.setOctets(freshestCrlBytes);
freshestCRL.setCrlUrls(getCRLDistributionPointsUrls(freshestCrlBytes));
freshestCRL.checkCritical(certificateToken);
return freshestCRL;
}
return null;
}
/**
* Returns the key usage, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link KeyUsage}
*/
public static KeyUsage getKeyUsage(CertificateToken certificateToken) {
final boolean[] keyUsageArray = certificateToken.getCertificate().getKeyUsage();
if (keyUsageArray != null) {
final KeyUsage keyUsage = new KeyUsage();
keyUsage.setOctets(certificateToken.getCertificate().getExtensionValue(keyUsage.getOid()));
final List keyUsageBits = new ArrayList<>();
for (KeyUsageBit keyUsageBit : KeyUsageBit.values()) {
if (keyUsageArray[keyUsageBit.getIndex()]) {
keyUsageBits.add(keyUsageBit);
}
}
keyUsage.setKeyUsageBits(keyUsageBits);
keyUsage.checkCritical(certificateToken);
return keyUsage;
}
return null;
}
/**
* Returns the extended key usage, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link ExtendedKeyUsages}
*/
public static ExtendedKeyUsages getExtendedKeyUsage(CertificateToken certificateToken) {
try {
final ExtendedKeyUsages extendedKeyUsage = new ExtendedKeyUsages();
extendedKeyUsage.setOctets(certificateToken.getCertificate().getExtensionValue(extendedKeyUsage.getOid()));
extendedKeyUsage.setOids(certificateToken.getCertificate().getExtendedKeyUsage());
extendedKeyUsage.checkCritical(certificateToken);
return extendedKeyUsage;
} catch (CertificateParsingException e) {
LOG.warn("Unable to retrieve ExtendedKeyUsage : {}", e.getMessage());
return null;
}
}
/**
* Returns the certificate policies, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link CertificatePolicies}
*/
public static CertificatePolicies getCertificatePolicies(CertificateToken certificateToken) {
final byte[] certificatePoliciesBinaries = certificateToken.getCertificate()
.getExtensionValue(CertificateExtensionEnum.CERTIFICATE_POLICIES.getOid());
if (Utils.isArrayNotEmpty(certificatePoliciesBinaries)) {
final CertificatePolicies certificatePolicies = new CertificatePolicies();
certificatePolicies.setOctets(certificatePoliciesBinaries);
final List policiesList = new ArrayList<>();
try {
ASN1Sequence seq = DSSASN1Utils.getAsn1SequenceFromDerOctetString(certificatePoliciesBinaries);
for (int ii = 0; ii < seq.size(); ii++) {
policiesList.add(getCertificatePolicy(seq.getObjectAt(ii)));
}
certificatePolicies.setPolicyList(policiesList);
certificatePolicies.checkCritical(certificateToken);
return certificatePolicies;
} catch (Exception e) {
LOG.warn("Unable to parse the certificatePolicies extension '{}' : {}", Utils.toBase64(certificatePoliciesBinaries), e.getMessage(), e);
}
}
return null;
}
private static CertificatePolicy getCertificatePolicy(ASN1Encodable policyObject) {
final CertificatePolicy cp = new CertificatePolicy();
final PolicyInformation policyInfo = PolicyInformation.getInstance(policyObject);
cp.setOid(policyInfo.getPolicyIdentifier().getId());
ASN1Sequence policyQualifiersSeq = policyInfo.getPolicyQualifiers();
if (policyQualifiersSeq != null) {
for (int jj = 0; jj < policyQualifiersSeq.size(); jj++) {
PolicyQualifierInfo pqi = PolicyQualifierInfo.getInstance(policyQualifiersSeq.getObjectAt(jj));
if (PolicyQualifierId.id_qt_cps.equals(pqi.getPolicyQualifierId())) {
cp.setCpsUrl(DSSASN1Utils.getString(pqi.getQualifier()));
}
}
}
return cp;
}
/**
* Returns the ocsp-nocheck extension value, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link OCSPNoCheck}
*/
public static OCSPNoCheck getOcspNoCheck(CertificateToken certificateToken) {
final byte[] extensionValue = certificateToken.getCertificate().getExtensionValue(OCSPObjectIdentifiers.id_pkix_ocsp_nocheck.getId());
if (extensionValue != null) {
final OCSPNoCheck ocspNoCheck = new OCSPNoCheck();
ocspNoCheck.setOctets(extensionValue);
ocspNoCheck.setOcspNoCheck(isNullIdentifiedValuePresent(extensionValue));
ocspNoCheck.checkCritical(certificateToken);
return ocspNoCheck;
}
return null;
}
/**
* Checks if the certificate contains ocsp-nocheck extension indicating if the revocation data
* should be checked for an OCSP signing certificate.
* RFC 6960
*
* @param certificateToken
* the certificate to be checked
* @return true if the certificate has the id_pkix_ocsp_nocheck extension
*/
public static boolean hasOcspNoCheckExtension(CertificateToken certificateToken) {
OCSPNoCheck ocspNoCheck = getOcspNoCheck(certificateToken);
return ocspNoCheck != null && ocspNoCheck.isOcspNoCheck();
}
/**
* Returns the ext-etsi-valassured-ST-certs extension value, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link ValidityAssuredShortTerm}
*/
public static ValidityAssuredShortTerm getValAssuredSTCerts(CertificateToken certificateToken) {
final byte[] extensionValue = certificateToken.getCertificate().getExtensionValue(OID.id_etsi_ext_valassured_ST_certs.getId());
if (extensionValue != null) {
final ValidityAssuredShortTerm validityAssuredShortTerm = new ValidityAssuredShortTerm();
validityAssuredShortTerm.setOctets(extensionValue);
validityAssuredShortTerm.setValAssuredSTCerts(isNullIdentifiedValuePresent(extensionValue));
validityAssuredShortTerm.checkCritical(certificateToken);
return validityAssuredShortTerm;
}
return null;
}
private static boolean isNullIdentifiedValuePresent(final byte[] extensionValue) {
try {
final ASN1Primitive derObject = DSSASN1Utils.toASN1Primitive(extensionValue);
if (derObject instanceof DEROctetString) {
return DSSASN1Utils.isDEROctetStringNull((DEROctetString) derObject);
}
} catch (Exception e) {
LOG.debug("Exception when processing 'id_pkix_ocsp_no_check'", e);
}
return false;
}
/**
* Returns the noRevAvail extension value, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link NoRevAvail}
*/
public static NoRevAvail getNoRevAvail(CertificateToken certificateToken) {
final byte[] extensionValue = certificateToken.getCertificate().getExtensionValue(Extension.noRevAvail.getId());
if (extensionValue != null) {
final NoRevAvail noRevAvail = new NoRevAvail();
noRevAvail.setOctets(extensionValue);
noRevAvail.setNoRevAvail(isNullIdentifiedValuePresent(extensionValue));
noRevAvail.checkCritical(certificateToken);
return noRevAvail;
}
return null;
}
/**
* Checks if the certificate contains ext-etsi-valassured-ST-certs extension indicating
* that the validity of the certificate is assured because the certificate is a "short-term certificate".
* That is, the time as indicated in the certificate attribute from notBefore through notAfter,
* inclusive, is shorter than the maximum time to process a revocation request as specified by
* the certificate practice statement or certificate policy.
*
* @param certificateToken {@link CertificateToken}
* @return TRUE if the certificate has ext-etsi-valassured-ST-certs extension, FALSE otherwise
*/
public static boolean hasValAssuredShortTermCertsExtension(CertificateToken certificateToken) {
ValidityAssuredShortTerm valAssuredSTCerts = getValAssuredSTCerts(certificateToken);
return valAssuredSTCerts != null && valAssuredSTCerts.isValAssuredSTCerts();
}
/**
* Returns the qc-statements extension value, when present
*
* @param certificateToken {@link CertificateToken}
* @return {@link QcStatements}
*/
public static QcStatements getQcStatements(CertificateToken certificateToken) {
final QcStatements qcStatements = QcStatementUtils.getQcStatements(certificateToken);
if (qcStatements != null) {
qcStatements.checkCritical(certificateToken);
}
return qcStatements;
}
/**
* Returns a certificate extension for an unsupported OID
*
* @param certificateToken {@link CertificateToken}
* @param oid {@link String} of the found certificate extension
* @return {@link CertificateExtension}
*/
private static CertificateExtension getOtherCertificateExtension(CertificateToken certificateToken, String oid) {
CertificateExtension certificateExtension;
CertificateExtensionEnum value = CertificateExtensionEnum.forOid(oid);
if (value != null) {
certificateExtension = new CertificateExtension(value);
} else {
certificateExtension = new CertificateExtension(oid);
}
certificateExtension.setOctets(certificateToken.getCertificate().getExtensionValue(oid));
certificateExtension.checkCritical(certificateToken);
if (value == null) {
if (certificateExtension.isCritical()) {
LOG.warn("Unknown critical CertificateExtension with OID : '{}'", oid);
} else if (LOG.isDebugEnabled()) {
LOG.debug("Unknown non-critical CertificateExtension with OID : '{}'", oid);
}
}
return certificateExtension;
}
}