/* * * Copyright (c) 2013 - 2017 Lijun Liao * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License version 3 * as published by the Free Software Foundation with the addition of the * following permission added to Section 15 as permitted in Section 7(a): * * FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY * THE AUTHOR LIJUN LIAO. LIJUN LIAO DISCLAIMS THE WARRANTY OF NON INFRINGEMENT * OF THIRD PARTY RIGHTS. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under * Section 5 of the GNU Affero General Public License. * * You can be released from the requirements of the license by purchasing * a commercial license. Buying such a license is mandatory as soon as you * develop commercial activities involving the XiPKI software without * disclosing the source code of your own applications. * * For more information, please contact Lijun Liao at this * address: lijun.liao@gmail.com */ package org.xipki.pki.ca.server.impl; import java.security.cert.X509Certificate; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TimeZone; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1ObjectIdentifier; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERNull; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.x500.RDN; import org.bouncycastle.asn1.x500.X500Name; import org.bouncycastle.asn1.x500.style.IETFUtils; import org.bouncycastle.asn1.x509.AccessDescription; import org.bouncycastle.asn1.x509.AuthorityInformationAccess; import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier; import org.bouncycastle.asn1.x509.BasicConstraints; import org.bouncycastle.asn1.x509.CRLDistPoint; import org.bouncycastle.asn1.x509.ExtendedKeyUsage; import org.bouncycastle.asn1.x509.Extension; import org.bouncycastle.asn1.x509.Extensions; import org.bouncycastle.asn1.x509.GeneralName; import org.bouncycastle.asn1.x509.GeneralNames; import org.bouncycastle.asn1.x509.KeyPurposeId; import org.bouncycastle.asn1.x509.SubjectKeyIdentifier; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.xipki.commons.common.util.CollectionUtil; import org.xipki.commons.common.util.ParamUtil; import org.xipki.commons.security.ExtensionExistence; import org.xipki.commons.security.HashAlgoType; import org.xipki.commons.security.KeyUsage; import org.xipki.commons.security.ObjectIdentifiers; import org.xipki.commons.security.util.X509Util; import org.xipki.pki.ca.api.BadCertTemplateException; import org.xipki.pki.ca.api.BadFormatException; import org.xipki.pki.ca.api.EnvParameterResolver; import org.xipki.pki.ca.api.NameId; import org.xipki.pki.ca.api.profile.CertValidity; import org.xipki.pki.ca.api.profile.CertprofileException; import org.xipki.pki.ca.api.profile.ExtensionControl; import org.xipki.pki.ca.api.profile.ExtensionValue; import org.xipki.pki.ca.api.profile.ExtensionValues; import org.xipki.pki.ca.api.profile.GeneralNameMode; import org.xipki.pki.ca.api.profile.x509.AuthorityInfoAccessControl; import org.xipki.pki.ca.api.profile.x509.ExtKeyUsageControl; import org.xipki.pki.ca.api.profile.x509.KeyUsageControl; import org.xipki.pki.ca.api.profile.x509.SpecialX509CertprofileBehavior; import org.xipki.pki.ca.api.profile.x509.SubjectDnSpec; import org.xipki.pki.ca.api.profile.x509.SubjectInfo; import org.xipki.pki.ca.api.profile.x509.X509CertLevel; import org.xipki.pki.ca.api.profile.x509.X509CertVersion; import org.xipki.pki.ca.api.profile.x509.X509Certprofile; import org.xipki.pki.ca.api.profile.x509.X509CertprofileUtil; import org.xipki.pki.ca.server.impl.util.CaUtil; import org.xipki.pki.ca.server.mgmt.api.CertprofileEntry; /** * @author Lijun Liao * @since 2.0.0 */ class IdentifiedX509Certprofile { private static final Set<ASN1ObjectIdentifier> CRITICAL_ONLY_EXTENSION_TYPES; private static final Set<ASN1ObjectIdentifier> CA_CRITICAL_ONLY_EXTENSION_TYPES; private static final Set<ASN1ObjectIdentifier> NONCRITICAL_ONLY_EXTENSION_TYPES; private static final Set<ASN1ObjectIdentifier> CA_ONLY_EXTENSION_TYPES; private static final Set<ASN1ObjectIdentifier> NONE_REQUEST_EXTENSION_TYPES; private static final Set<ASN1ObjectIdentifier> REQUIRED_CA_EXTENSION_TYPES; private static final Set<ASN1ObjectIdentifier> REQUIRED_EE_EXTENSION_TYPES; static { CRITICAL_ONLY_EXTENSION_TYPES = new HashSet<>(); CRITICAL_ONLY_EXTENSION_TYPES.add(Extension.keyUsage); CRITICAL_ONLY_EXTENSION_TYPES.add(Extension.policyMappings); CRITICAL_ONLY_EXTENSION_TYPES.add(Extension.nameConstraints); CRITICAL_ONLY_EXTENSION_TYPES.add(Extension.policyConstraints); CRITICAL_ONLY_EXTENSION_TYPES.add(Extension.inhibitAnyPolicy); CRITICAL_ONLY_EXTENSION_TYPES.add(ObjectIdentifiers.id_pe_tlsfeature); CA_CRITICAL_ONLY_EXTENSION_TYPES = new HashSet<>(); CA_CRITICAL_ONLY_EXTENSION_TYPES.add(Extension.basicConstraints); NONCRITICAL_ONLY_EXTENSION_TYPES = new HashSet<>(); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.authorityKeyIdentifier); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.subjectKeyIdentifier); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.issuerAlternativeName); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.subjectDirectoryAttributes); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.freshestCRL); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.authorityInfoAccess); NONCRITICAL_ONLY_EXTENSION_TYPES.add(Extension.subjectInfoAccess); CA_ONLY_EXTENSION_TYPES = new HashSet<>(); CA_ONLY_EXTENSION_TYPES.add(Extension.policyMappings); CA_ONLY_EXTENSION_TYPES.add(Extension.nameConstraints); CA_ONLY_EXTENSION_TYPES.add(Extension.policyConstraints); CA_ONLY_EXTENSION_TYPES.add(Extension.inhibitAnyPolicy); NONE_REQUEST_EXTENSION_TYPES = new HashSet<ASN1ObjectIdentifier>(); NONE_REQUEST_EXTENSION_TYPES.add(Extension.subjectKeyIdentifier); NONE_REQUEST_EXTENSION_TYPES.add(Extension.authorityKeyIdentifier); NONE_REQUEST_EXTENSION_TYPES.add(Extension.issuerAlternativeName); NONE_REQUEST_EXTENSION_TYPES.add(Extension.cRLDistributionPoints); NONE_REQUEST_EXTENSION_TYPES.add(Extension.freshestCRL); NONE_REQUEST_EXTENSION_TYPES.add(Extension.basicConstraints); NONE_REQUEST_EXTENSION_TYPES.add(Extension.inhibitAnyPolicy); REQUIRED_CA_EXTENSION_TYPES = new HashSet<>(); REQUIRED_CA_EXTENSION_TYPES.add(Extension.basicConstraints); REQUIRED_CA_EXTENSION_TYPES.add(Extension.subjectKeyIdentifier); REQUIRED_CA_EXTENSION_TYPES.add(Extension.keyUsage); REQUIRED_EE_EXTENSION_TYPES = new HashSet<>(); REQUIRED_EE_EXTENSION_TYPES.add(Extension.authorityKeyIdentifier); REQUIRED_EE_EXTENSION_TYPES.add(Extension.subjectKeyIdentifier); } // end static private final CertprofileEntry dbEntry; private final X509Certprofile certprofile; IdentifiedX509Certprofile(final CertprofileEntry dbEntry, final X509Certprofile certProfile) throws CertprofileException { this.dbEntry = ParamUtil.requireNonNull("entry", dbEntry); this.certprofile = ParamUtil.requireNonNull("certProfile", certProfile); this.certprofile.initialize(dbEntry.getConf()); if (certProfile.getSpecialCertprofileBehavior() == SpecialX509CertprofileBehavior.gematik_gSMC_K) { String paramName = SpecialX509CertprofileBehavior.PARAMETER_MAXLIFTIME; String str = certProfile.getParameter(paramName); if (str == null) { throw new CertprofileException("parameter " + paramName + " is not defined"); } str = str.trim(); int idx; try { idx = Integer.parseInt(str); } catch (NumberFormatException ex) { throw new CertprofileException("invalid " + paramName + ": " + str); } if (idx < 1) { throw new CertprofileException("invalid " + paramName + ": " + str); } } } // constructor public NameId getIdent() { return dbEntry.getIdent(); } public CertprofileEntry getDbEntry() { return dbEntry; } public X509CertVersion getVersion() { return certprofile.getVersion(); } public List<String> getSignatureAlgorithms() { return certprofile.getSignatureAlgorithms(); } public SpecialX509CertprofileBehavior getSpecialCertprofileBehavior() { return certprofile.getSpecialCertprofileBehavior(); } public void setEnvParameterResolver(final EnvParameterResolver envParameterResolver) { if (certprofile != null) { certprofile.setEnvParameterResolver(envParameterResolver); } } public Date getNotBefore(final Date notBefore) { return certprofile.getNotBefore(notBefore); } public CertValidity getValidity() { return certprofile.getValidity(); } public boolean hasMidnightNotBefore() { return certprofile.hasMidnightNotBefore(); } public TimeZone getTimezone() { return certprofile.getTimezone(); } public SubjectInfo getSubject(final X500Name requestedSubject) throws CertprofileException, BadCertTemplateException { SubjectInfo subjectInfo = certprofile.getSubject(requestedSubject); RDN[] countryRdns = subjectInfo.getGrantedSubject().getRDNs(ObjectIdentifiers.DN_C); if (countryRdns != null) { for (RDN rdn : countryRdns) { String textValue = IETFUtils.valueToString(rdn.getFirst().getValue()); if (!SubjectDnSpec.isValidCountryAreaCode(textValue)) { throw new BadCertTemplateException("invalid country/area code '" + textValue + "'"); } } } return subjectInfo; } public ExtensionValues getExtensions(@NonNull final X500Name requestedSubject, @NonNull final X500Name grantedSubject, @Nullable final Extensions requestedExtensions, @NonNull final SubjectPublicKeyInfo publicKeyInfo, @NonNull final PublicCaInfo publicCaInfo, @Nullable final X509Certificate crlSignerCert, @NonNull final Date notBefore, @NonNull final Date notAfter) throws CertprofileException, BadCertTemplateException { ParamUtil.requireNonNull("publicKeyInfo", publicKeyInfo); ExtensionValues values = new ExtensionValues(); Map<ASN1ObjectIdentifier, ExtensionControl> controls = new HashMap<>(certprofile.getExtensionControls()); Set<ASN1ObjectIdentifier> neededExtTypes = new HashSet<>(); Set<ASN1ObjectIdentifier> wantedExtTypes = new HashSet<>(); if (requestedExtensions != null) { Extension reqExtension = requestedExtensions.getExtension( ObjectIdentifiers.id_xipki_ext_cmpRequestExtensions); if (reqExtension != null) { ExtensionExistence ee = ExtensionExistence.getInstance( reqExtension.getParsedValue()); neededExtTypes.addAll(ee.getNeedExtensions()); wantedExtTypes.addAll(ee.getWantExtensions()); } for (ASN1ObjectIdentifier oid : neededExtTypes) { if (wantedExtTypes.contains(oid)) { wantedExtTypes.remove(oid); } if (!controls.containsKey(oid)) { throw new BadCertTemplateException( "could not add needed extension " + oid.getId()); } } } // SubjectKeyIdentifier ASN1ObjectIdentifier extType = Extension.subjectKeyIdentifier; ExtensionControl extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { byte[] encodedSpki = publicKeyInfo.getPublicKeyData().getBytes(); byte[] skiValue = HashAlgoType.SHA1.hash(encodedSpki); SubjectKeyIdentifier value = new SubjectKeyIdentifier(skiValue); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // Authority key identifier extType = Extension.authorityKeyIdentifier; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { byte[] ikiValue = publicCaInfo.getSubjectKeyIdentifer(); AuthorityKeyIdentifier value = null; if (ikiValue != null) { if (certprofile.includeIssuerAndSerialInAki()) { GeneralNames x509CaSubject = new GeneralNames( new GeneralName(publicCaInfo.getX500Subject())); value = new AuthorityKeyIdentifier(ikiValue, x509CaSubject, publicCaInfo.getSerialNumber()); } else { value = new AuthorityKeyIdentifier(ikiValue); } } addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // IssuerAltName extType = Extension.issuerAlternativeName; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { GeneralNames value = publicCaInfo.getSubjectAltName(); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // AuthorityInfoAccess extType = Extension.authorityInfoAccess; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { AuthorityInfoAccessControl aiaControl = certprofile.getAiaControl(); List<String> caIssuers = null; if (aiaControl == null || aiaControl.includesCaIssuers()) { caIssuers = publicCaInfo.getCaCertUris(); } List<String> ocspUris = null; if (aiaControl == null || aiaControl.includesOcsp()) { ocspUris = publicCaInfo.getOcspUris(); } if (CollectionUtil.isNonEmpty(caIssuers) || CollectionUtil.isNonEmpty(ocspUris)) { AuthorityInformationAccess value = CaUtil.createAuthorityInformationAccess( caIssuers, ocspUris); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } } if (controls.containsKey(Extension.cRLDistributionPoints) || controls.containsKey(Extension.freshestCRL)) { X500Name crlSignerSubject = (crlSignerCert == null) ? null : X500Name.getInstance(crlSignerCert.getSubjectX500Principal().getEncoded()); X500Name x500CaPrincipal = publicCaInfo.getX500Subject(); // CRLDistributionPoints extType = Extension.cRLDistributionPoints; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { if (CollectionUtil.isNonEmpty(publicCaInfo.getCrlUris())) { CRLDistPoint value = CaUtil.createCrlDistributionPoints( publicCaInfo.getCrlUris(), x500CaPrincipal, crlSignerSubject); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } } // FreshestCRL extType = Extension.freshestCRL; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { if (CollectionUtil.isNonEmpty(publicCaInfo.getDeltaCrlUris())) { CRLDistPoint value = CaUtil.createCrlDistributionPoints( publicCaInfo.getDeltaCrlUris(), x500CaPrincipal, crlSignerSubject); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } } } // BasicConstraints extType = Extension.basicConstraints; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { BasicConstraints value = CaUtil.createBasicConstraints(certprofile.getCertLevel(), certprofile.getPathLenBasicConstraint()); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // KeyUsage extType = Extension.keyUsage; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { Set<KeyUsage> usages = new HashSet<>(); Set<KeyUsageControl> usageOccs = certprofile.getKeyUsage(); for (KeyUsageControl k : usageOccs) { if (k.isRequired()) { usages.add(k.getKeyUsage()); } } // the optional KeyUsage will only be set if requested explicitly if (requestedExtensions != null && extControl.isRequest()) { addRequestedKeyusage(usages, requestedExtensions, usageOccs); } org.bouncycastle.asn1.x509.KeyUsage value = X509Util.createKeyUsage(usages); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // ExtendedKeyUsage extType = Extension.extendedKeyUsage; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { List<ASN1ObjectIdentifier> usages = new LinkedList<>(); Set<ExtKeyUsageControl> usageOccs = certprofile.getExtendedKeyUsages(); for (ExtKeyUsageControl k : usageOccs) { if (k.isRequired()) { usages.add(k.getExtKeyUsage()); } } // the optional ExtKeyUsage will only be set if requested explicitly if (requestedExtensions != null && extControl.isRequest()) { addRequestedExtKeyusage(usages, requestedExtensions, usageOccs); } if (extControl.isCritical() && usages.contains(ObjectIdentifiers.id_anyExtendedKeyUsage)) { extControl = new ExtensionControl(false, extControl.isRequired(), extControl.isRequest()); } ExtendedKeyUsage value = X509Util.createExtendedUsage(usages); addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // ocsp-nocheck extType = ObjectIdentifiers.id_extension_pkix_ocsp_nocheck; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { // the extension ocsp-nocheck will only be set if requested explicitly DERNull value = DERNull.INSTANCE; addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } // SubjectInfoAccess extType = Extension.subjectInfoAccess; extControl = controls.remove(extType); if (extControl != null && addMe(extType, extControl, neededExtTypes, wantedExtTypes)) { ASN1Sequence value = null; if (requestedExtensions != null && extControl.isRequest()) { value = createSubjectInfoAccess(requestedExtensions, certprofile.getSubjectInfoAccessModes()); } addExtension(values, extType, value, extControl, neededExtTypes, wantedExtTypes); } ExtensionValues subvalues = certprofile.getExtensions( Collections.unmodifiableMap(controls), requestedSubject, grantedSubject, requestedExtensions, notBefore, notAfter); Set<ASN1ObjectIdentifier> extTypes = new HashSet<>(controls.keySet()); for (ASN1ObjectIdentifier type : extTypes) { extControl = controls.remove(type); boolean addMe = addMe(type, extControl, neededExtTypes, wantedExtTypes); if (addMe) { ExtensionValue value = null; if (requestedExtensions != null && extControl.isRequest()) { Extension reqExt = requestedExtensions.getExtension(type); if (reqExt != null) { value = new ExtensionValue(reqExt.isCritical(), reqExt.getParsedValue()); } } if (value == null) { value = subvalues.getExtensionValue(type); } addExtension(values, type, value, extControl, neededExtTypes, wantedExtTypes); } } Set<ASN1ObjectIdentifier> unprocessedExtTypes = new HashSet<>(); for (ASN1ObjectIdentifier type : controls.keySet()) { if (controls.get(type).isRequired()) { unprocessedExtTypes.add(type); } } if (CollectionUtil.isNonEmpty(unprocessedExtTypes)) { throw new CertprofileException( "could not add required extensions " + toString(unprocessedExtTypes)); } if (CollectionUtil.isNonEmpty(neededExtTypes)) { throw new BadCertTemplateException( "could not add requested extensions " + toString(neededExtTypes)); } return values; } // method getExtensions public X509CertLevel getCertLevel() { return certprofile.getCertLevel(); } public boolean isOnlyForRa() { return certprofile.isOnlyForRa(); } public SubjectPublicKeyInfo checkPublicKey(final SubjectPublicKeyInfo publicKey) throws BadCertTemplateException { ParamUtil.requireNonNull("publicKey", publicKey); return certprofile.checkPublicKey(publicKey); } public boolean incSerialNumberIfSubjectExists() { return certprofile.incSerialNumberIfSubjectExists(); } public void shutdown() { if (certprofile != null) { certprofile.shutdown(); } } public boolean includeIssuerAndSerialInAki() { return certprofile.includeIssuerAndSerialInAki(); } public String incSerialNumber(final String currentSerialNumber) throws BadFormatException { return certprofile.incSerialNumber(currentSerialNumber); } public boolean isDuplicateKeyPermitted() { return certprofile.isDuplicateKeyPermitted(); } public boolean isDuplicateSubjectPermitted() { return certprofile.isDuplicateSubjectPermitted(); } public boolean isSerialNumberInReqPermitted() { return certprofile.isSerialNumberInReqPermitted(); } public String getParameter(final String paramName) { return certprofile.getParameter(paramName); } public Map<ASN1ObjectIdentifier, ExtensionControl> getExtensionControls() { return certprofile.getExtensionControls(); } public Set<KeyUsageControl> getKeyUsage() { return certprofile.getKeyUsage(); } public Integer getPathLenBasicConstraint() { return certprofile.getPathLenBasicConstraint(); } public Set<ExtKeyUsageControl> getExtendedKeyUsages() { return certprofile.getExtendedKeyUsages(); } public int getMaxCertSize() { return certprofile.getMaxCertSize(); } public void validate() throws CertprofileException { StringBuilder msg = new StringBuilder(); Map<ASN1ObjectIdentifier, ExtensionControl> controls = getExtensionControls(); // make sure that non-request extensions are not permitted in requests Set<ASN1ObjectIdentifier> set = new HashSet<>(); for (ASN1ObjectIdentifier type : NONE_REQUEST_EXTENSION_TYPES) { ExtensionControl control = controls.get(type); if (control != null && control.isRequest()) { set.add(type); } } if (CollectionUtil.isNonEmpty(set)) { msg.append("extensions ").append(toString(set)); msg.append(" must not be contained in request, "); } X509CertLevel level = getCertLevel(); boolean ca = (level == X509CertLevel.RootCA) || (level == X509CertLevel.SubCA); // make sure that CA-only extensions are not permitted in EE certificate set.clear(); if (!ca) { set.clear(); for (ASN1ObjectIdentifier type : CA_ONLY_EXTENSION_TYPES) { if (controls.containsKey(type)) { set.add(type); } } if (CollectionUtil.isNonEmpty(set)) { msg.append("EE profile contains CA-only extensions ").append(toString(set)) .append(", "); } } // make sure that critical only extensions are not marked as non-critical. set.clear(); for (ASN1ObjectIdentifier type : controls.keySet()) { ExtensionControl control = controls.get(type); if (CRITICAL_ONLY_EXTENSION_TYPES.contains(type)) { if (!control.isCritical()) { set.add(type); } } if (ca && CA_CRITICAL_ONLY_EXTENSION_TYPES.contains(type)) { if (!control.isCritical()) { set.add(type); } } } if (CollectionUtil.isNonEmpty(set)) { msg.append("critical only extensions are marked as non-critical "); msg.append(toString(set)).append(", "); } // make sure that non-critical only extensions are not marked as critical. set.clear(); for (ASN1ObjectIdentifier type : controls.keySet()) { ExtensionControl control = controls.get(type); if (NONCRITICAL_ONLY_EXTENSION_TYPES.contains(type)) { if (control.isCritical()) { set.add(type); } } } if (CollectionUtil.isNonEmpty(set)) { msg.append("non-critical extensions are marked as critical ").append(toString(set)); msg.append(", "); } // make sure that required extensions are present set.clear(); Set<ASN1ObjectIdentifier> requiredTypes = ca ? REQUIRED_CA_EXTENSION_TYPES : REQUIRED_EE_EXTENSION_TYPES; for (ASN1ObjectIdentifier type : requiredTypes) { ExtensionControl extCtrl = controls.get(type); if (extCtrl == null || !extCtrl.isRequired()) { set.add(type); } } if (level == X509CertLevel.SubCA) { ASN1ObjectIdentifier type = Extension.authorityKeyIdentifier; ExtensionControl extCtrl = controls.get(type); if (extCtrl == null || !extCtrl.isRequired()) { set.add(type); } } if (!set.isEmpty()) { msg.append("required extensions are not marked as required "); msg.append(toString(set)).append(", "); } // KeyUsage Set<KeyUsageControl> usages = getKeyUsage(); if (ca) { // make sure the CA certificate contains usage keyCertSign if (!containsKeyusage(usages, KeyUsage.keyCertSign)) { msg.append("CA profile does not contain keyUsage "); msg.append(KeyUsage.keyCertSign).append(", "); } } else { // make sure the EE certificate does not contain CA-only usages KeyUsage[] caOnlyUsages = new KeyUsage[] {KeyUsage.keyCertSign, KeyUsage.cRLSign}; Set<KeyUsage> setUsages = new HashSet<>(); for (KeyUsage caOnlyUsage : caOnlyUsages) { if (containsKeyusage(usages, caOnlyUsage)) { setUsages.add(caOnlyUsage); } } if (CollectionUtil.isNonEmpty(set)) { msg.append("EE profile contains CA-only keyUsage ").append(setUsages).append(", "); } } final int len = msg.length(); if (len > 2) { msg.delete(len - 2, len); throw new CertprofileException(msg.toString()); } } // method validate private static String toString(final Set<ASN1ObjectIdentifier> oids) { if (oids == null) { return "null"; } StringBuilder sb = new StringBuilder(); sb.append("["); for (ASN1ObjectIdentifier oid : oids) { String name = ObjectIdentifiers.getName(oid); if (name != null) { sb.append(name); sb.append(" (").append(oid.getId()).append(")"); } else { sb.append(oid.getId()); } sb.append(", "); } if (CollectionUtil.isNonEmpty(oids)) { int len = sb.length(); sb.delete(len - 2, len); } sb.append("]"); return sb.toString(); } // method toString private static boolean containsKeyusage(final Set<KeyUsageControl> usageControls, final KeyUsage usage) { for (KeyUsageControl entry : usageControls) { if (usage == entry.getKeyUsage()) { return true; } } return false; } private static boolean addMe(final ASN1ObjectIdentifier extType, final ExtensionControl extControl, final Set<ASN1ObjectIdentifier> neededExtTypes, final Set<ASN1ObjectIdentifier> wantedExtTypes) { boolean addMe = extControl.isRequired(); if (addMe) { return true; } return neededExtTypes.contains(extType) || wantedExtTypes.contains(extType); } // method addMe private static void addRequestedKeyusage(final Set<KeyUsage> usages, final Extensions requestedExtensions, final Set<KeyUsageControl> usageOccs) { Extension extension = requestedExtensions.getExtension(Extension.keyUsage); if (extension == null) { return; } org.bouncycastle.asn1.x509.KeyUsage reqKeyUsage = org.bouncycastle.asn1.x509.KeyUsage.getInstance(extension.getParsedValue()); for (KeyUsageControl k : usageOccs) { if (k.isRequired()) { continue; } if (reqKeyUsage.hasUsages(k.getKeyUsage().getBcUsage())) { usages.add(k.getKeyUsage()); } } } // method addRequestedKeyusage private static void addRequestedExtKeyusage(final List<ASN1ObjectIdentifier> usages, final Extensions requestedExtensions, final Set<ExtKeyUsageControl> usageOccs) { Extension extension = requestedExtensions.getExtension(Extension.extendedKeyUsage); if (extension == null) { return; } ExtendedKeyUsage reqKeyUsage = ExtendedKeyUsage.getInstance(extension.getParsedValue()); for (ExtKeyUsageControl k : usageOccs) { if (k.isRequired()) { continue; } if (reqKeyUsage.hasKeyPurposeId(KeyPurposeId.getInstance(k.getExtKeyUsage()))) { usages.add(k.getExtKeyUsage()); } } } // method addRequestedExtKeyusage private static ASN1Sequence createSubjectInfoAccess(final Extensions requestedExtensions, final Map<ASN1ObjectIdentifier, Set<GeneralNameMode>> modes) throws BadCertTemplateException { if (modes == null) { return null; } ASN1Encodable extValue = requestedExtensions.getExtensionParsedValue( Extension.subjectInfoAccess); if (extValue == null) { return null; } ASN1Sequence reqSeq = ASN1Sequence.getInstance(extValue); int size = reqSeq.size(); ASN1EncodableVector vec = new ASN1EncodableVector(); for (int i = 0; i < size; i++) { AccessDescription ad = AccessDescription.getInstance(reqSeq.getObjectAt(i)); ASN1ObjectIdentifier accessMethod = ad.getAccessMethod(); Set<GeneralNameMode> generalNameModes = modes.get(accessMethod); if (generalNameModes == null) { throw new BadCertTemplateException("subjectInfoAccess.accessMethod " + accessMethod.getId() + " is not allowed"); } GeneralName accessLocation = X509CertprofileUtil.createGeneralName( ad.getAccessLocation(), generalNameModes); vec.add(new AccessDescription(accessMethod, accessLocation)); } // end for return vec.size() > 0 ? new DERSequence(vec) : null; } // method createSubjectInfoAccess private static void addExtension(final ExtensionValues values, final ASN1ObjectIdentifier extType, final ExtensionValue extValue, final ExtensionControl extControl, final Set<ASN1ObjectIdentifier> neededExtensionTypes, final Set<ASN1ObjectIdentifier> wantedExtensionTypes) throws CertprofileException { if (extValue != null) { values.addExtension(extType, extValue); neededExtensionTypes.remove(extType); wantedExtensionTypes.remove(extType); return; } if (!extControl.isRequired()) { return; } String description = ObjectIdentifiers.getName(extType); if (description == null) { description = extType.getId(); } throw new CertprofileException("could not add required extension " + description); } // method addExtension private static void addExtension(final ExtensionValues values, final ASN1ObjectIdentifier extType, final ASN1Encodable extValue, final ExtensionControl extControl, final Set<ASN1ObjectIdentifier> neededExtensionTypes, final Set<ASN1ObjectIdentifier> wantedExtensionTypes) throws CertprofileException { if (extValue != null) { values.addExtension(extType, extControl.isCritical(), extValue); neededExtensionTypes.remove(extType); wantedExtensionTypes.remove(extType); return; } if (!extControl.isRequired()) { return; } String description = ObjectIdentifiers.getName(extType); if (description == null) { description = extType.getId(); } throw new CertprofileException("could not add required extension " + description); } // method addExtension }