/*
*
* 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.qa;
import java.io.IOException;
import java.math.BigInteger;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
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.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1GeneralizedTime;
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.ASN1StreamParser;
import org.bouncycastle.asn1.ASN1String;
import org.bouncycastle.asn1.ASN1TaggedObject;
import org.bouncycastle.asn1.DERBMPString;
import org.bouncycastle.asn1.DERIA5String;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERT61String;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.DERUTF8String;
import org.bouncycastle.asn1.isismtt.x509.Admissions;
import org.bouncycastle.asn1.isismtt.x509.ProfessionInfo;
import org.bouncycastle.asn1.x500.DirectoryString;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.AccessDescription;
import org.bouncycastle.asn1.x509.Attribute;
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.CertPolicyId;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.DistributionPoint;
import org.bouncycastle.asn1.x509.DistributionPointName;
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.GeneralSubtree;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.PolicyQualifierId;
import org.bouncycastle.asn1.x509.PolicyQualifierInfo;
import org.bouncycastle.asn1.x509.SubjectDirectoryAttributes;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.UserNotice;
import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
import org.bouncycastle.asn1.x509.qualified.BiometricData;
import org.bouncycastle.asn1.x509.qualified.Iso4217CurrencyCode;
import org.bouncycastle.asn1.x509.qualified.MonetaryValue;
import org.bouncycastle.asn1.x509.qualified.QCStatement;
import org.bouncycastle.asn1.x509.qualified.TypeOfBiometricData;
import org.bouncycastle.util.encoders.Hex;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.commons.common.qa.ValidationIssue;
import org.xipki.commons.common.util.CollectionUtil;
import org.xipki.commons.common.util.CompareUtil;
import org.xipki.commons.common.util.LogUtil;
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.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.GeneralNameMode;
import org.xipki.pki.ca.api.profile.GeneralNameTag;
import org.xipki.pki.ca.api.profile.Range;
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.SubjectDirectoryAttributesControl;
import org.xipki.pki.ca.api.profile.x509.SubjectDnSpec;
import org.xipki.pki.ca.api.profile.x509.X509CertLevel;
import org.xipki.pki.ca.certprofile.BiometricInfoOption;
import org.xipki.pki.ca.certprofile.XmlX509Certprofile;
import org.xipki.pki.ca.certprofile.XmlX509CertprofileUtil;
import org.xipki.pki.ca.certprofile.commonpki.AdmissionSyntaxOption;
import org.xipki.pki.ca.certprofile.x509.jaxb.AdditionalInformation;
import org.xipki.pki.ca.certprofile.x509.jaxb.AuthorizationTemplate;
import org.xipki.pki.ca.certprofile.x509.jaxb.ConstantExtValue;
import org.xipki.pki.ca.certprofile.x509.jaxb.ExtensionType;
import org.xipki.pki.ca.certprofile.x509.jaxb.ExtensionsType;
import org.xipki.pki.ca.certprofile.x509.jaxb.InhibitAnyPolicy;
import org.xipki.pki.ca.certprofile.x509.jaxb.PdsLocationType;
import org.xipki.pki.ca.certprofile.x509.jaxb.PdsLocationsType;
import org.xipki.pki.ca.certprofile.x509.jaxb.PolicyConstraints;
import org.xipki.pki.ca.certprofile.x509.jaxb.PolicyMappings;
import org.xipki.pki.ca.certprofile.x509.jaxb.QcEuLimitValueType;
import org.xipki.pki.ca.certprofile.x509.jaxb.QcStatementType;
import org.xipki.pki.ca.certprofile.x509.jaxb.QcStatementValueType;
import org.xipki.pki.ca.certprofile.x509.jaxb.QcStatements;
import org.xipki.pki.ca.certprofile.x509.jaxb.Range2Type;
import org.xipki.pki.ca.certprofile.x509.jaxb.RangeType;
import org.xipki.pki.ca.certprofile.x509.jaxb.RangesType;
import org.xipki.pki.ca.certprofile.x509.jaxb.Restriction;
import org.xipki.pki.ca.certprofile.x509.jaxb.SMIMECapabilities;
import org.xipki.pki.ca.certprofile.x509.jaxb.SMIMECapability;
import org.xipki.pki.ca.certprofile.x509.jaxb.TlsFeature;
import org.xipki.pki.ca.certprofile.x509.jaxb.TripleState;
import org.xipki.pki.ca.certprofile.x509.jaxb.ValidityModel;
import org.xipki.pki.ca.certprofile.x509.jaxb.X509ProfileType;
import org.xipki.pki.ca.qa.internal.QaAuthorizationTemplate;
import org.xipki.pki.ca.qa.internal.QaCertificatePolicies;
import org.xipki.pki.ca.qa.internal.QaCertificatePolicies.QaCertificatePolicyInformation;
import org.xipki.pki.ca.qa.internal.QaDirectoryString;
import org.xipki.pki.ca.qa.internal.QaExtensionValue;
import org.xipki.pki.ca.qa.internal.QaGeneralSubtree;
import org.xipki.pki.ca.qa.internal.QaInhibitAnyPolicy;
import org.xipki.pki.ca.qa.internal.QaNameConstraints;
import org.xipki.pki.ca.qa.internal.QaPolicyConstraints;
import org.xipki.pki.ca.qa.internal.QaPolicyMappingsOption;
import org.xipki.pki.ca.qa.internal.QaPolicyQualifierInfo;
import org.xipki.pki.ca.qa.internal.QaPolicyQualifierInfo.QaCpsUriPolicyQualifier;
import org.xipki.pki.ca.qa.internal.QaPolicyQualifierInfo.QaUserNoticePolicyQualifierInfo;
import org.xipki.pki.ca.qa.internal.QaPolicyQualifiers;
import org.xipki.pki.ca.qa.internal.QaTlsFeature;
/**
* @author Lijun Liao
* @since 2.0.0
*/
public class ExtensionsChecker {
private static final byte[] DER_NULL = new byte[]{5, 0};
private static final Logger LOG = LoggerFactory.getLogger(ExtensionsChecker.class);
private static final List<String> ALL_USAGES = Arrays.asList(
KeyUsage.digitalSignature.getName(), // 0
KeyUsage.contentCommitment.getName(), // 1
KeyUsage.keyEncipherment.getName(), // 2
KeyUsage.dataEncipherment.getName(), // 3
KeyUsage.keyAgreement.getName(), // 4
KeyUsage.keyCertSign.getName(), // 5
KeyUsage.cRLSign.getName(), // 6
KeyUsage.encipherOnly.getName(), // 7
KeyUsage.decipherOnly.getName() // 8
);
private QaCertificatePolicies certificatePolicies;
private QaPolicyMappingsOption policyMappings;
private QaNameConstraints nameConstraints;
private QaPolicyConstraints policyConstraints;
private QaInhibitAnyPolicy inhibitAnyPolicy;
private QaDirectoryString restriction;
private QaDirectoryString additionalInformation;
private ASN1ObjectIdentifier validityModelId;
private QcStatements qcStatements;
private QaAuthorizationTemplate authorizationTemplate;
private QaTlsFeature tlsFeature;
private QaExtensionValue smimeCapabilities;
private Map<ASN1ObjectIdentifier, QaExtensionValue> constantExtensions;
private XmlX509Certprofile certProfile;
public ExtensionsChecker(final X509ProfileType conf, final XmlX509Certprofile certProfile)
throws CertprofileException {
this.certProfile = ParamUtil.requireNonNull("certProfile", certProfile);
ParamUtil.requireNonNull("conf", conf);
// Extensions
ExtensionsType extensionsType = conf.getExtensions();
// Extension controls
Map<ASN1ObjectIdentifier, ExtensionControl> extensionControls =
certProfile.getExtensionControls();
// Certificate Policies
ASN1ObjectIdentifier type = Extension.certificatePolicies;
if (extensionControls.containsKey(type)) {
org.xipki.pki.ca.certprofile.x509.jaxb.CertificatePolicies extConf =
(org.xipki.pki.ca.certprofile.x509.jaxb.CertificatePolicies)
getExtensionValue(type, extensionsType,
org.xipki.pki.ca.certprofile.x509.jaxb.CertificatePolicies.class);
if (extConf != null) {
this.certificatePolicies = new QaCertificatePolicies(extConf);
}
}
// Policy Mappings
type = Extension.policyMappings;
if (extensionControls.containsKey(type)) {
PolicyMappings extConf = (PolicyMappings) getExtensionValue(
type, extensionsType, PolicyMappings.class);
if (extConf != null) {
this.policyMappings = new QaPolicyMappingsOption(extConf);
}
}
// Name Constrains
type = Extension.nameConstraints;
if (extensionControls.containsKey(type)) {
org.xipki.pki.ca.certprofile.x509.jaxb.NameConstraints extConf =
(org.xipki.pki.ca.certprofile.x509.jaxb.NameConstraints) getExtensionValue(
type, extensionsType,
org.xipki.pki.ca.certprofile.x509.jaxb.NameConstraints.class);
if (extConf != null) {
this.nameConstraints = new QaNameConstraints(extConf);
}
}
// Policy Constraints
type = Extension.policyConstraints;
if (extensionControls.containsKey(type)) {
PolicyConstraints extConf = (PolicyConstraints) getExtensionValue(
type, extensionsType, PolicyConstraints.class);
if (extConf != null) {
this.policyConstraints = new QaPolicyConstraints(extConf);
}
}
// Inhibit anyPolicy
type = Extension.inhibitAnyPolicy;
if (extensionControls.containsKey(type)) {
InhibitAnyPolicy extConf = (InhibitAnyPolicy) getExtensionValue(
type, extensionsType, InhibitAnyPolicy.class);
if (extConf != null) {
this.inhibitAnyPolicy = new QaInhibitAnyPolicy(extConf);
}
}
// restriction
type = ObjectIdentifiers.id_extension_restriction;
if (extensionControls.containsKey(type)) {
Restriction extConf = (Restriction) getExtensionValue(
type, extensionsType, Restriction.class);
if (extConf != null) {
restriction = new QaDirectoryString(
XmlX509CertprofileUtil.convertDirectoryStringType(extConf.getType()),
extConf.getText());
}
}
// additionalInformation
type = ObjectIdentifiers.id_extension_additionalInformation;
if (extensionControls.containsKey(type)) {
AdditionalInformation extConf = (AdditionalInformation) getExtensionValue(
type, extensionsType, AdditionalInformation.class);
if (extConf != null) {
additionalInformation = new QaDirectoryString(
XmlX509CertprofileUtil.convertDirectoryStringType(extConf.getType()),
extConf.getText());
}
}
// validityModel
type = ObjectIdentifiers.id_extension_validityModel;
if (extensionControls.containsKey(type)) {
ValidityModel extConf = (ValidityModel) getExtensionValue(
type, extensionsType, ValidityModel.class);
if (extConf != null) {
validityModelId = new ASN1ObjectIdentifier(extConf.getModelId().getValue());
}
}
// QCStatements
type = Extension.qCStatements;
if (extensionControls.containsKey(type)) {
QcStatements extConf = (QcStatements) getExtensionValue(
type, extensionsType, QcStatements.class);
if (extConf != null) {
qcStatements = extConf;
}
}
// tlsFeature
type = ObjectIdentifiers.id_pe_tlsfeature;
if (extensionControls.containsKey(type)) {
TlsFeature extConf = (TlsFeature) getExtensionValue(
type, extensionsType, TlsFeature.class);
if (extConf != null) {
tlsFeature = new QaTlsFeature(extConf);
}
}
// AuthorizationTemplate
type = ObjectIdentifiers.id_xipki_ext_authorizationTemplate;
if (extensionControls.containsKey(type)) {
AuthorizationTemplate extConf = (AuthorizationTemplate) getExtensionValue(
type, extensionsType, AuthorizationTemplate.class);
if (extConf != null) {
authorizationTemplate = new QaAuthorizationTemplate(extConf);
}
}
// SMIMECapabilities
type = ObjectIdentifiers.id_smimeCapabilities;
if (extensionControls.containsKey(type)) {
SMIMECapabilities extConf = (SMIMECapabilities) getExtensionValue(
type, extensionsType, SMIMECapabilities.class);
List<SMIMECapability> list = extConf.getSMIMECapability();
ASN1EncodableVector vec = new ASN1EncodableVector();
for (SMIMECapability m : list) {
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(
m.getCapabilityID().getValue());
ASN1Encodable params = null;
org.xipki.pki.ca.certprofile.x509.jaxb.SMIMECapability.Parameters capParams =
m.getParameters();
if (capParams != null) {
if (capParams.getInteger() != null) {
params = new ASN1Integer(capParams.getInteger());
} else if (capParams.getBase64Binary() != null) {
params = readAsn1Encodable(capParams.getBase64Binary().getValue());
}
}
org.bouncycastle.asn1.smime.SMIMECapability cap =
new org.bouncycastle.asn1.smime.SMIMECapability(oid, params);
vec.add(cap);
}
DERSequence extValue = new DERSequence(vec);
try {
smimeCapabilities = new QaExtensionValue(
extensionControls.get(type).isCritical(), extValue.getEncoded());
} catch (IOException ex) {
throw new CertprofileException(
"Cannot encode SMIMECapabilities: " + ex.getMessage());
}
}
// constant extensions
this.constantExtensions = buildConstantExtesions(extensionsType);
} // constructor
public List<ValidationIssue> checkExtensions(final Certificate cert,
final X509IssuerInfo issuerInfo, final Extensions requestedExtensions,
final X500Name requestedSubject) {
ParamUtil.requireNonNull("cert", cert);
ParamUtil.requireNonNull("issuerInfo", issuerInfo);
X509Certificate jceCert;
try {
jceCert = X509Util.toX509Cert(cert);
} catch (CertificateException ex) {
throw new IllegalArgumentException("invalid cert: " + ex.getMessage());
}
List<ValidationIssue> result = new LinkedList<>();
// detect the list of extension types in certificate
Set<ASN1ObjectIdentifier> presentExtenionTypes =
getExensionTypes(cert, issuerInfo, requestedExtensions);
Extensions extensions = cert.getTBSCertificate().getExtensions();
ASN1ObjectIdentifier[] oids = extensions.getExtensionOIDs();
if (oids == null) {
ValidationIssue issue = new ValidationIssue("X509.EXT.GEN", "extension general");
result.add(issue);
issue.setFailureMessage("no extension is present");
return result;
}
List<ASN1ObjectIdentifier> certExtTypes = Arrays.asList(oids);
for (ASN1ObjectIdentifier extType : presentExtenionTypes) {
if (!certExtTypes.contains(extType)) {
ValidationIssue issue = createExtensionIssue(extType);
result.add(issue);
issue.setFailureMessage("extension is absent but is required");
}
}
Map<ASN1ObjectIdentifier, ExtensionControl> extensionControls =
certProfile.getExtensionControls();
for (ASN1ObjectIdentifier oid : certExtTypes) {
ValidationIssue issue = createExtensionIssue(oid);
result.add(issue);
if (!presentExtenionTypes.contains(oid)) {
issue.setFailureMessage("extension is present but is not permitted");
continue;
}
Extension ext = extensions.getExtension(oid);
StringBuilder failureMsg = new StringBuilder();
ExtensionControl extControl = extensionControls.get(oid);
if (extControl.isCritical() != ext.isCritical()) {
addViolation(failureMsg, "critical", ext.isCritical(), extControl.isCritical());
}
byte[] extensionValue = ext.getExtnValue().getOctets();
try {
if (Extension.authorityKeyIdentifier.equals(oid)) {
// AuthorityKeyIdentifier
checkExtensionIssuerKeyIdentifier(failureMsg, extensionValue, issuerInfo);
} else if (Extension.subjectKeyIdentifier.equals(oid)) {
// SubjectKeyIdentifier
checkExtensionSubjectKeyIdentifier(failureMsg, extensionValue,
cert.getSubjectPublicKeyInfo());
} else if (Extension.keyUsage.equals(oid)) {
// KeyUsage
checkExtensionKeyUsage(failureMsg, extensionValue, jceCert.getKeyUsage(),
requestedExtensions, extControl);
} else if (Extension.certificatePolicies.equals(oid)) {
// CertificatePolicies
checkExtensionCertificatePolicies(failureMsg, extensionValue,
requestedExtensions, extControl);
} else if (Extension.policyMappings.equals(oid)) {
// Policy Mappings
checkExtensionPolicyMappings(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (Extension.subjectAlternativeName.equals(oid)) {
// SubjectAltName
checkExtensionSubjectAltName(failureMsg, extensionValue, requestedExtensions,
extControl, requestedSubject);
} else if (Extension.subjectDirectoryAttributes.equals(oid)) {
// SubjectDirectoryAttributes
checkExtensionSubjectDirAttrs(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (Extension.issuerAlternativeName.equals(oid)) {
// IssuerAltName
checkExtensionIssuerAltNames(failureMsg, extensionValue, issuerInfo);
} else if (Extension.basicConstraints.equals(oid)) {
// Basic Constraints
checkExtensionBasicConstraints(failureMsg, extensionValue);
} else if (Extension.nameConstraints.equals(oid)) {
// Name Constraints
checkExtensionNameConstraints(failureMsg, extensionValue, extensions,
extControl);
} else if (Extension.policyConstraints.equals(oid)) {
// PolicyConstrains
checkExtensionPolicyConstraints(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (Extension.extendedKeyUsage.equals(oid)) {
// ExtendedKeyUsage
checkExtensionExtendedKeyUsage(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (Extension.cRLDistributionPoints.equals(oid)) {
// CRL Distribution Points
checkExtensionCrlDistributionPoints(failureMsg, extensionValue, issuerInfo);
} else if (Extension.inhibitAnyPolicy.equals(oid)) {
// Inhibit anyPolicy
checkExtensionInhibitAnyPolicy(failureMsg, extensionValue, extensions,
extControl);
} else if (Extension.freshestCRL.equals(oid)) {
// Freshest CRL
checkExtensionDeltaCrlDistributionPoints(failureMsg, extensionValue,
issuerInfo);
} else if (Extension.authorityInfoAccess.equals(oid)) {
// Authority Information Access
checkExtensionAuthorityInfoAccess(failureMsg, extensionValue, issuerInfo);
} else if (Extension.subjectInfoAccess.equals(oid)) {
// SubjectInfoAccess
checkExtensionSubjectInfoAccess(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (ObjectIdentifiers.id_extension_admission.equals(oid)) {
// Admission
checkExtensionAdmission(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (ObjectIdentifiers.id_extension_pkix_ocsp_nocheck.equals(oid)) {
// ocsp-nocheck
checkExtensionOcspNocheck(failureMsg, extensionValue);
} else if (ObjectIdentifiers.id_extension_restriction.equals(oid)) {
// restriction
checkExtensionRestriction(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (ObjectIdentifiers.id_extension_additionalInformation.equals(oid)) {
// additionalInformation
checkExtensionAdditionalInformation(failureMsg, extensionValue,
requestedExtensions, extControl);
} else if (ObjectIdentifiers.id_extension_validityModel.equals(oid)) {
// validityModel
checkExtensionValidityModel(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (Extension.privateKeyUsagePeriod.equals(oid)) {
// privateKeyUsagePeriod
checkExtensionPrivateKeyUsagePeriod(failureMsg, extensionValue,
jceCert.getNotBefore(), jceCert.getNotAfter());
} else if (Extension.qCStatements.equals(oid)) {
// qCStatements
checkExtensionQcStatements(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (Extension.biometricInfo.equals(oid)) {
// biometricInfo
checkExtensionBiometricInfo(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (ObjectIdentifiers.id_pe_tlsfeature.equals(oid)) {
// tlsFeature
checkExtensionTlsFeature(failureMsg, extensionValue, requestedExtensions,
extControl);
} else if (ObjectIdentifiers.id_xipki_ext_authorizationTemplate.equals(oid)) {
// authorizationTemplate
checkExtensionAuthorizationTemplate(failureMsg, extensionValue,
requestedExtensions, extControl);
} else {
byte[] expected;
if (ObjectIdentifiers.id_smimeCapabilities.equals(oid)) {
// SMIMECapabilities
expected = smimeCapabilities.getValue();
} else {
expected = getExpectedExtValue(oid, requestedExtensions, extControl);
}
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension valus", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
}
if (failureMsg.length() > 0) {
issue.setFailureMessage(failureMsg.toString());
}
} catch (IllegalArgumentException | ClassCastException
| ArrayIndexOutOfBoundsException ex) {
LOG.debug("extension value does not have correct syntax", ex);
issue.setFailureMessage("extension value does not have correct syntax");
}
}
return result;
} // method checkExtensions
private byte[] getExpectedExtValue(final ASN1ObjectIdentifier type,
final Extensions requestedExtensions, final ExtensionControl extControl) {
if (constantExtensions != null && constantExtensions.containsKey(type)) {
return constantExtensions.get(type).getValue();
} else if (requestedExtensions != null && extControl.isRequest()) {
Extension reqExt = requestedExtensions.getExtension(type);
if (reqExt != null) {
return reqExt.getExtnValue().getOctets();
}
}
return null;
} // getExpectedExtValue
private Set<ASN1ObjectIdentifier> getExensionTypes(final Certificate cert,
final X509IssuerInfo issuerInfo, final Extensions requestedExtensions) {
Set<ASN1ObjectIdentifier> types = new HashSet<>();
// profile required extension types
Map<ASN1ObjectIdentifier, ExtensionControl> extensionControls =
certProfile.getExtensionControls();
for (ASN1ObjectIdentifier oid : extensionControls.keySet()) {
if (extensionControls.get(oid).isRequired()) {
types.add(oid);
}
}
Set<ASN1ObjectIdentifier> wantedExtensionTypes = new HashSet<>();
if (requestedExtensions != null) {
Extension reqExtension = requestedExtensions.getExtension(
ObjectIdentifiers.id_xipki_ext_cmpRequestExtensions);
if (reqExtension != null) {
ExtensionExistence ee = ExtensionExistence.getInstance(
reqExtension.getParsedValue());
types.addAll(ee.getNeedExtensions());
wantedExtensionTypes.addAll(ee.getWantExtensions());
}
}
if (CollectionUtil.isEmpty(wantedExtensionTypes)) {
return types;
}
// wanted extension types
// Authority key identifier
ASN1ObjectIdentifier type = Extension.authorityKeyIdentifier;
if (wantedExtensionTypes.contains(type)) {
types.add(type);
}
// Subject key identifier
type = Extension.subjectKeyIdentifier;
if (wantedExtensionTypes.contains(type)) {
types.add(type);
}
// KeyUsage
type = Extension.keyUsage;
if (wantedExtensionTypes.contains(type)) {
boolean required = false;
if (requestedExtensions != null && requestedExtensions.getExtension(type) != null) {
required = true;
}
if (!required) {
Set<KeyUsageControl> requiredKeyusage = getKeyusage(true);
if (CollectionUtil.isNonEmpty(requiredKeyusage)) {
required = true;
}
}
if (required) {
types.add(type);
}
}
// CertificatePolicies
type = Extension.certificatePolicies;
if (wantedExtensionTypes.contains(type)) {
if (certificatePolicies != null) {
types.add(type);
}
}
// Policy Mappings
type = Extension.policyMappings;
if (wantedExtensionTypes.contains(type)) {
if (policyMappings != null) {
types.add(type);
}
}
// SubjectAltNames
type = Extension.subjectAlternativeName;
if (wantedExtensionTypes.contains(type)) {
if (requestedExtensions != null && requestedExtensions.getExtension(type) != null) {
types.add(type);
}
}
// IssuerAltName
type = Extension.issuerAlternativeName;
if (wantedExtensionTypes.contains(type)) {
if (cert.getTBSCertificate().getExtensions().getExtension(
Extension.subjectAlternativeName) != null) {
types.add(type);
}
}
// BasicConstraints
type = Extension.basicConstraints;
if (wantedExtensionTypes.contains(type)) {
types.add(type);
}
// Name Constraints
type = Extension.nameConstraints;
if (wantedExtensionTypes.contains(type)) {
if (nameConstraints != null) {
types.add(type);
}
}
// PolicyConstrains
type = Extension.policyConstraints;
if (wantedExtensionTypes.contains(type)) {
if (policyConstraints != null) {
types.add(type);
}
}
// ExtendedKeyUsage
type = Extension.extendedKeyUsage;
if (wantedExtensionTypes.contains(type)) {
boolean required = false;
if (requestedExtensions != null && requestedExtensions.getExtension(type) != null) {
required = true;
}
if (!required) {
Set<ExtKeyUsageControl> requiredExtKeyusage = getExtKeyusage(true);
if (CollectionUtil.isNonEmpty(requiredExtKeyusage)) {
required = true;
}
}
if (required) {
types.add(type);
}
}
// CRLDistributionPoints
type = Extension.cRLDistributionPoints;
if (wantedExtensionTypes.contains(type)) {
if (issuerInfo.getCrlUrls() != null) {
types.add(type);
}
}
// Inhibit anyPolicy
type = Extension.inhibitAnyPolicy;
if (wantedExtensionTypes.contains(type)) {
if (inhibitAnyPolicy != null) {
types.add(type);
}
}
// FreshestCRL
type = Extension.freshestCRL;
if (wantedExtensionTypes.contains(type)) {
if (issuerInfo.getDeltaCrlUrls() != null) {
types.add(type);
}
}
// AuthorityInfoAccess
type = Extension.authorityInfoAccess;
if (wantedExtensionTypes.contains(type)) {
if (issuerInfo.getOcspUrls() != null) {
types.add(type);
}
}
// SubjectInfoAccess
type = Extension.subjectInfoAccess;
if (wantedExtensionTypes.contains(type)) {
if (requestedExtensions != null && requestedExtensions.getExtension(type) != null) {
types.add(type);
}
}
// Admission
type = ObjectIdentifiers.id_extension_admission;
if (wantedExtensionTypes.contains(type)) {
if (certProfile.getAdmission() != null) {
types.add(type);
}
}
// ocsp-nocheck
type = ObjectIdentifiers.id_extension_pkix_ocsp_nocheck;
if (wantedExtensionTypes.contains(type)) {
types.add(type);
}
wantedExtensionTypes.removeAll(types);
for (ASN1ObjectIdentifier oid : wantedExtensionTypes) {
if (requestedExtensions != null && requestedExtensions.getExtension(oid) != null) {
if (constantExtensions.containsKey(oid)) {
types.add(oid);
}
}
}
return types;
} // method getExensionTypes
private ValidationIssue createExtensionIssue(final ASN1ObjectIdentifier extId) {
ValidationIssue issue;
String extName = ObjectIdentifiers.getName(extId);
if (extName == null) {
extName = extId.getId().replace('.', '_');
issue = new ValidationIssue("X509.EXT." + extName, "extension " + extId.getId());
} else {
issue = new ValidationIssue("X509.EXT." + extName, "extension " + extName
+ " (" + extId.getId() + ")");
}
return issue;
} // method createExtensionIssue
private void checkExtensionBasicConstraints(final StringBuilder failureMsg,
final byte[] extensionValue) {
BasicConstraints bc = BasicConstraints.getInstance(extensionValue);
X509CertLevel certLevel = certProfile.getCertLevel();
boolean ca = (X509CertLevel.RootCA == certLevel) || (X509CertLevel.SubCA == certLevel);
if (ca != bc.isCA()) {
addViolation(failureMsg, "ca", bc.isCA(), ca);
}
if (bc.isCA()) {
BigInteger tmpPathLen = bc.getPathLenConstraint();
Integer pathLen = certProfile.getPathLen();
if (pathLen == null) {
if (tmpPathLen != null) {
addViolation(failureMsg, "pathLen", tmpPathLen, "absent");
}
} else {
if (tmpPathLen == null) {
addViolation(failureMsg, "pathLen", "null", pathLen);
} else if (!BigInteger.valueOf(pathLen).equals(tmpPathLen)) {
addViolation(failureMsg, "pathLen", tmpPathLen, pathLen);
}
}
}
} // method checkExtensionBasicConstraints
private void checkExtensionSubjectKeyIdentifier(final StringBuilder failureMsg,
final byte[] extensionValue, final SubjectPublicKeyInfo subjectPublicKeyInfo) {
// subjectKeyIdentifier
SubjectKeyIdentifier asn1 = SubjectKeyIdentifier.getInstance(extensionValue);
byte[] ski = asn1.getKeyIdentifier();
byte[] pkData = subjectPublicKeyInfo.getPublicKeyData().getBytes();
byte[] expectedSki = HashAlgoType.SHA1.hash(pkData);
if (!Arrays.equals(expectedSki, ski)) {
addViolation(failureMsg, "SKI", hex(ski), hex(expectedSki));
}
} // method checkExtensionSubjectKeyIdentifier
private void checkExtensionIssuerKeyIdentifier(final StringBuilder failureMsg,
final byte[] extensionValue, final X509IssuerInfo issuerInfo) {
AuthorityKeyIdentifier asn1 = AuthorityKeyIdentifier.getInstance(extensionValue);
byte[] keyIdentifier = asn1.getKeyIdentifier();
if (keyIdentifier == null) {
failureMsg.append("keyIdentifier is 'absent' but expected 'present'; ");
} else if (!Arrays.equals(issuerInfo.getSubjectKeyIdentifier(), keyIdentifier)) {
addViolation(failureMsg, "keyIdentifier", hex(keyIdentifier),
hex(issuerInfo.getSubjectKeyIdentifier()));
}
BigInteger serialNumber = asn1.getAuthorityCertSerialNumber();
GeneralNames names = asn1.getAuthorityCertIssuer();
if (certProfile.isIncludeIssuerAndSerialInAki()) {
if (serialNumber == null) {
failureMsg.append("authorityCertSerialNumber is 'absent' but expected 'present'; ");
} else {
if (!issuerInfo.getCert().getSerialNumber().equals(serialNumber)) {
addViolation(failureMsg, "authorityCertSerialNumber",
LogUtil.formatCsn(serialNumber),
LogUtil.formatCsn(issuerInfo.getCert().getSerialNumber()));
}
}
if (names == null) {
failureMsg.append("authorityCertIssuer is 'absent' but expected 'present'; ");
} else {
GeneralName[] genNames = names.getNames();
X500Name x500GenName = null;
for (GeneralName genName : genNames) {
if (genName.getTagNo() != GeneralName.directoryName) {
continue;
}
if (x500GenName != null) {
failureMsg.append("authorityCertIssuer contains at least two ");
failureMsg.append("directoryName but expected one; ");
break;
} else {
x500GenName = (X500Name) genName.getName();
}
}
if (x500GenName == null) {
failureMsg.append(
"authorityCertIssuer does not contain directoryName but expected one; ");
} else {
X500Name caSubject = issuerInfo.getBcCert().getTBSCertificate().getSubject();
if (!caSubject.equals(x500GenName)) {
addViolation(failureMsg, "authorityCertIssuer", x500GenName, caSubject);
}
}
}
} else {
if (serialNumber != null) {
failureMsg.append("authorityCertSerialNumber is 'absent' but expected 'present'; ");
}
if (names != null) {
failureMsg.append("authorityCertIssuer is 'absent' but expected 'present'; ");
}
}
} // method checkExtensionIssuerKeyIdentifier
private void checkExtensionNameConstraints(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaNameConstraints conf = nameConstraints;
if (conf == null) {
byte[] expected = getExpectedExtValue(Extension.nameConstraints, requestedExtensions,
extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
org.bouncycastle.asn1.x509.NameConstraints tmpNameConstraints =
org.bouncycastle.asn1.x509.NameConstraints.getInstance(extensionValue);
checkExtensionNameConstraintsSubtrees(failureMsg, "PermittedSubtrees",
tmpNameConstraints.getPermittedSubtrees(), conf.getPermittedSubtrees());
checkExtensionNameConstraintsSubtrees(failureMsg, "ExcludedSubtrees",
tmpNameConstraints.getExcludedSubtrees(), conf.getExcludedSubtrees());
} // method checkExtensionNameConstraints
private void checkExtensionNameConstraintsSubtrees(final StringBuilder failureMsg,
final String description, final GeneralSubtree[] subtrees,
final List<QaGeneralSubtree> expectedSubtrees) {
int isSize = (subtrees == null) ? 0 : subtrees.length;
int expSize = (expectedSubtrees == null) ? 0 : expectedSubtrees.size();
if (isSize != expSize) {
addViolation(failureMsg, "size of " + description, isSize, expSize);
return;
}
if (subtrees == null || expectedSubtrees == null) {
return;
}
for (int i = 0; i < isSize; i++) {
GeneralSubtree isSubtree = subtrees[i];
QaGeneralSubtree expSubtree = expectedSubtrees.get(i);
BigInteger bigInt = isSubtree.getMinimum();
int isMinimum = (bigInt == null) ? 0 : bigInt.intValue();
Integer minimum = expSubtree.getMinimum();
int expMinimum = (minimum == null) ? 0 : minimum.intValue();
String desc = description + " [" + i + "]";
if (isMinimum != expMinimum) {
addViolation(failureMsg, "minimum of " + desc, isMinimum, expMinimum);
}
bigInt = isSubtree.getMaximum();
Integer isMaximum = (bigInt == null) ? null : bigInt.intValue();
Integer expMaximum = expSubtree.getMaximum();
if (!CompareUtil.equalsObject(isMaximum, expMaximum)) {
addViolation(failureMsg, "maxmum of " + desc, isMaximum, expMaximum);
}
GeneralName isBase = isSubtree.getBase();
GeneralName expBase;
if (expSubtree.getDirectoryName() != null) {
expBase = new GeneralName(X509Util.reverse(
new X500Name(expSubtree.getDirectoryName())));
} else if (expSubtree.getDnsName() != null) {
expBase = new GeneralName(GeneralName.dNSName, expSubtree.getDnsName());
} else if (expSubtree.getIpAddress() != null) {
expBase = new GeneralName(GeneralName.iPAddress, expSubtree.getIpAddress());
} else if (expSubtree.getRfc822Name() != null) {
expBase = new GeneralName(GeneralName.rfc822Name, expSubtree.getRfc822Name());
} else if (expSubtree.getUri() != null) {
expBase = new GeneralName(GeneralName.uniformResourceIdentifier,
expSubtree.getUri());
} else {
throw new RuntimeException("should not reach here, unknown child of GeneralName");
}
if (!isBase.equals(expBase)) {
addViolation(failureMsg, "base of " + desc, isBase, expBase);
}
}
} // method checkExtensionNameConstraintsSubtrees
private void checkExtensionPolicyConstraints(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaPolicyConstraints conf = policyConstraints;
if (conf == null) {
byte[] expected = getExpectedExtValue(Extension.policyConstraints,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
org.bouncycastle.asn1.x509.PolicyConstraints isPolicyConstraints =
org.bouncycastle.asn1.x509.PolicyConstraints.getInstance(extensionValue);
Integer expRequireExplicitPolicy = conf.getRequireExplicitPolicy();
BigInteger bigInt = isPolicyConstraints.getRequireExplicitPolicyMapping();
Integer isRequireExplicitPolicy = (bigInt == null) ? null : bigInt.intValue();
boolean match = true;
if (expRequireExplicitPolicy == null) {
if (isRequireExplicitPolicy != null) {
match = false;
}
} else if (!expRequireExplicitPolicy.equals(isRequireExplicitPolicy)) {
match = false;
}
if (!match) {
addViolation(failureMsg, "requireExplicitPolicy", isRequireExplicitPolicy,
expRequireExplicitPolicy);
}
Integer expInhibitPolicyMapping = conf.getInhibitPolicyMapping();
bigInt = isPolicyConstraints.getInhibitPolicyMapping();
Integer isInhibitPolicyMapping = (bigInt == null) ? null : bigInt.intValue();
match = true;
if (expInhibitPolicyMapping == null) {
if (isInhibitPolicyMapping != null) {
match = false;
}
} else if (!expInhibitPolicyMapping.equals(isInhibitPolicyMapping)) {
match = false;
}
if (!match) {
addViolation(failureMsg, "inhibitPolicyMapping", isInhibitPolicyMapping,
expInhibitPolicyMapping);
}
} // method checkExtensionPolicyConstraints
private void checkExtensionKeyUsage(final StringBuilder failureMsg, final byte[] extensionValue,
final boolean[] usages, final Extensions requestedExtensions,
final ExtensionControl extControl) {
int len = usages.length;
if (len > 9) {
failureMsg.append("invalid syntax: size of valid bits is larger than 9: ").append(len);
failureMsg.append("; ");
}
Set<String> isUsages = new HashSet<>();
for (int i = 0; i < len; i++) {
if (usages[i]) {
isUsages.add(ALL_USAGES.get(i));
}
}
Set<String> expectedUsages = new HashSet<>();
Set<KeyUsageControl> requiredKeyusage = getKeyusage(true);
for (KeyUsageControl usage : requiredKeyusage) {
expectedUsages.add(usage.getKeyUsage().getName());
}
Set<KeyUsageControl> optionalKeyusage = getKeyusage(false);
if (requestedExtensions != null && extControl.isRequest()
&& CollectionUtil.isNonEmpty(optionalKeyusage)) {
Extension extension = requestedExtensions.getExtension(Extension.keyUsage);
if (extension != null) {
org.bouncycastle.asn1.x509.KeyUsage reqKeyUsage =
org.bouncycastle.asn1.x509.KeyUsage.getInstance(extension.getParsedValue());
for (KeyUsageControl k : optionalKeyusage) {
if (reqKeyUsage.hasUsages(k.getKeyUsage().getBcUsage())) {
expectedUsages.add(k.getKeyUsage().getName());
}
}
}
}
if (CollectionUtil.isEmpty(expectedUsages)) {
byte[] constantExtValue = getConstantExtensionValue(Extension.keyUsage);
if (constantExtValue != null) {
expectedUsages = getKeyUsage(constantExtValue);
}
}
Set<String> diffs = strInBnotInA(expectedUsages, isUsages);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("usages ").append(diffs.toString())
.append(" are present but not expected; ");
}
diffs = strInBnotInA(isUsages, expectedUsages);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("usages ").append(diffs.toString())
.append(" are absent but are required; ");
}
} // method checkExtensionKeyUsage
private void checkExtensionExtendedKeyUsage(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
Set<String> isUsages = new HashSet<>();
org.bouncycastle.asn1.x509.ExtendedKeyUsage keyusage =
org.bouncycastle.asn1.x509.ExtendedKeyUsage.getInstance(extensionValue);
KeyPurposeId[] usages = keyusage.getUsages();
if (usages != null) {
for (KeyPurposeId usage : usages) {
isUsages.add(usage.getId());
}
}
Set<String> expectedUsages = new HashSet<>();
Set<ExtKeyUsageControl> requiredExtKeyusage = getExtKeyusage(true);
if (requiredExtKeyusage != null) {
for (ExtKeyUsageControl usage : requiredExtKeyusage) {
expectedUsages.add(usage.getExtKeyUsage().getId());
}
}
Set<ExtKeyUsageControl> optionalExtKeyusage = getExtKeyusage(false);
if (requestedExtensions != null && extControl.isRequest()
&& CollectionUtil.isNonEmpty(optionalExtKeyusage)) {
Extension extension = requestedExtensions.getExtension(Extension.extendedKeyUsage);
if (extension != null) {
org.bouncycastle.asn1.x509.ExtendedKeyUsage reqKeyUsage =
org.bouncycastle.asn1.x509.ExtendedKeyUsage.getInstance(
extension.getParsedValue());
for (ExtKeyUsageControl k : optionalExtKeyusage) {
if (reqKeyUsage.hasKeyPurposeId(KeyPurposeId.getInstance(k.getExtKeyUsage()))) {
expectedUsages.add(k.getExtKeyUsage().getId());
}
}
}
}
if (CollectionUtil.isEmpty(expectedUsages)) {
byte[] constantExtValue = getConstantExtensionValue(Extension.keyUsage);
if (constantExtValue != null) {
expectedUsages = getExtKeyUsage(constantExtValue);
}
}
Set<String> diffs = strInBnotInA(expectedUsages, isUsages);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("usages ").append(diffs.toString())
.append(" are present but not expected; ");
}
diffs = strInBnotInA(isUsages, expectedUsages);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("usages ").append(diffs.toString())
.append(" are absent but are required; ");
}
} // method checkExtensionExtendedKeyUsage
private void checkExtensionTlsFeature(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaTlsFeature conf = tlsFeature;
if (conf == null) {
byte[] expected = getExpectedExtValue(ObjectIdentifiers.id_pe_tlsfeature,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
Set<String> isFeatures = new HashSet<>();
ASN1Sequence seq =
ASN1Sequence.getInstance(extensionValue);
final int n = seq.size();
for (int i = 0; i < n; i++) {
ASN1Integer asn1Feature = ASN1Integer.getInstance(seq.getObjectAt(i));
isFeatures.add(asn1Feature.getPositiveValue().toString());
}
Set<String> expFeatures = new HashSet<>();
for (Integer m : conf.getFeatures()) {
expFeatures.add(m.toString());
}
Set<String> diffs = strInBnotInA(expFeatures, isFeatures);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("features ").append(diffs.toString())
.append(" are present but not expected; ");
}
diffs = strInBnotInA(isFeatures, expFeatures);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("features ").append(diffs.toString())
.append(" are absent but are required; ");
}
} // method checkExtensionTlsFeature
private void checkExtensionCertificatePolicies(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaCertificatePolicies conf = certificatePolicies;
if (conf == null) {
byte[] expected = getExpectedExtValue(Extension.certificatePolicies,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
org.bouncycastle.asn1.x509.CertificatePolicies asn1 =
org.bouncycastle.asn1.x509.CertificatePolicies.getInstance(extensionValue);
PolicyInformation[] isPolicyInformations = asn1.getPolicyInformation();
for (PolicyInformation isPolicyInformation : isPolicyInformations) {
ASN1ObjectIdentifier isPolicyId = isPolicyInformation.getPolicyIdentifier();
QaCertificatePolicyInformation expCp = conf.getPolicyInformation(isPolicyId.getId());
if (expCp == null) {
failureMsg.append("certificate policy '").append(isPolicyId);
failureMsg.append("' is not expected; ");
continue;
}
QaPolicyQualifiers expCpPq = expCp.getPolicyQualifiers();
if (expCpPq == null) {
continue;
}
ASN1Sequence isPolicyQualifiers = isPolicyInformation.getPolicyQualifiers();
List<String> isCpsUris = new LinkedList<>();
List<String> isUserNotices = new LinkedList<>();
int size = isPolicyQualifiers.size();
for (int i = 0; i < size; i++) {
PolicyQualifierInfo isPolicyQualifierInfo =
(PolicyQualifierInfo) isPolicyQualifiers.getObjectAt(i);
ASN1ObjectIdentifier isPolicyQualifierId =
isPolicyQualifierInfo.getPolicyQualifierId();
ASN1Encodable isQualifier = isPolicyQualifierInfo.getQualifier();
if (PolicyQualifierId.id_qt_cps.equals(isPolicyQualifierId)) {
String isCpsUri = ((DERIA5String) isQualifier).getString();
isCpsUris.add(isCpsUri);
} else if (PolicyQualifierId.id_qt_unotice.equals(isPolicyQualifierId)) {
UserNotice isUserNotice = UserNotice.getInstance(isQualifier);
if (isUserNotice.getExplicitText() != null) {
isUserNotices.add(isUserNotice.getExplicitText().getString());
}
}
}
List<QaPolicyQualifierInfo> qualifierInfos = expCpPq.getPolicyQualifiers();
for (QaPolicyQualifierInfo qualifierInfo : qualifierInfos) {
if (qualifierInfo instanceof QaCpsUriPolicyQualifier) {
String value = ((QaCpsUriPolicyQualifier) qualifierInfo).getCpsUri();
if (!isCpsUris.contains(value)) {
failureMsg.append("CPSUri '").append(value);
failureMsg.append("' is absent but is required; ");
}
} else if (qualifierInfo instanceof QaUserNoticePolicyQualifierInfo) {
String value =
((QaUserNoticePolicyQualifierInfo) qualifierInfo).getUserNotice();
if (!isUserNotices.contains(value)) {
failureMsg.append("userNotice '").append(value);
failureMsg.append("' is absent but is required; ");
}
} else {
throw new RuntimeException("should not reach here");
}
}
}
for (QaCertificatePolicyInformation cp : conf.getPolicyInformations()) {
boolean present = false;
for (PolicyInformation isPolicyInformation : isPolicyInformations) {
if (isPolicyInformation.getPolicyIdentifier().getId().equals(cp.getPolicyId())) {
present = true;
break;
}
}
if (present) {
continue;
}
failureMsg.append("certificate policy '").append(cp.getPolicyId());
failureMsg.append("' is absent but is required; ");
}
} // method checkExtensionCertificatePolicies
private void checkExtensionPolicyMappings(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaPolicyMappingsOption conf = policyMappings;
if (conf == null) {
byte[] expected = getExpectedExtValue(Extension.policyMappings, requestedExtensions,
extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
ASN1Sequence isPolicyMappings = DERSequence.getInstance(extensionValue);
Map<String, String> isMap = new HashMap<>();
int size = isPolicyMappings.size();
for (int i = 0; i < size; i++) {
ASN1Sequence seq = ASN1Sequence.getInstance(isPolicyMappings.getObjectAt(i));
CertPolicyId issuerDomainPolicy = CertPolicyId.getInstance(seq.getObjectAt(0));
CertPolicyId subjectDomainPolicy = CertPolicyId.getInstance(seq.getObjectAt(1));
isMap.put(issuerDomainPolicy.getId(), subjectDomainPolicy.getId());
}
Set<String> expIssuerDomainPolicies = conf.getIssuerDomainPolicies();
for (String expIssuerDomainPolicy : expIssuerDomainPolicies) {
String expSubjectDomainPolicy = conf.getSubjectDomainPolicy(expIssuerDomainPolicy);
String isSubjectDomainPolicy = isMap.remove(expIssuerDomainPolicy);
if (isSubjectDomainPolicy == null) {
failureMsg.append("issuerDomainPolicy '").append(expIssuerDomainPolicy)
.append("' is absent but is required; ");
} else if (!isSubjectDomainPolicy.equals(expSubjectDomainPolicy)) {
addViolation(failureMsg, "subjectDomainPolicy for issuerDomainPolicy",
isSubjectDomainPolicy, expSubjectDomainPolicy);
}
}
if (CollectionUtil.isNonEmpty(isMap)) {
failureMsg.append("issuerDomainPolicies '").append(isMap.keySet());
failureMsg.append("' are present but not expected; ");
}
} // method checkExtensionPolicyMappings
private void checkExtensionInhibitAnyPolicy(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaInhibitAnyPolicy conf = inhibitAnyPolicy;
if (conf == null) {
byte[] expected = getExpectedExtValue(Extension.inhibitAnyPolicy,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", extensionValue,
(expected == null) ? "not present" : hex(expected));
}
return;
}
ASN1Integer asn1Int = ASN1Integer.getInstance(extensionValue);
int isSkipCerts = asn1Int.getPositiveValue().intValue();
if (isSkipCerts != conf.getSkipCerts()) {
addViolation(failureMsg, "skipCerts", isSkipCerts, conf.getSkipCerts());
}
} // method checkExtensionInhibitAnyPolicy
private void checkExtensionSubjectDirAttrs(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
SubjectDirectoryAttributesControl conf = certProfile.getSubjectDirAttrsControl();
if (conf == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
ASN1Encodable extInRequest = null;
if (requestedExtensions != null) {
extInRequest = requestedExtensions.getExtensionParsedValue(
Extension.subjectDirectoryAttributes);
}
if (extInRequest == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
SubjectDirectoryAttributes requested = SubjectDirectoryAttributes.getInstance(extInRequest);
Vector<?> reqSubDirAttrs = requested.getAttributes();
ASN1GeneralizedTime expDateOfBirth = null;
String expPlaceOfBirth = null;
String expGender = null;
Set<String> expCountryOfCitizenshipList = new HashSet<>();
Set<String> expCountryOfResidenceList = new HashSet<>();
Map<ASN1ObjectIdentifier, Set<ASN1Encodable>> expOtherAttrs = new HashMap<>();
final int expN = reqSubDirAttrs.size();
for (int i = 0; i < expN; i++) {
Attribute attr = Attribute.getInstance(reqSubDirAttrs.get(i));
ASN1ObjectIdentifier attrType = attr.getAttrType();
ASN1Encodable attrVal = attr.getAttributeValues()[0];
if (ObjectIdentifiers.DN_DATE_OF_BIRTH.equals(attrType)) {
expDateOfBirth = ASN1GeneralizedTime.getInstance(attrVal);
} else if (ObjectIdentifiers.DN_PLACE_OF_BIRTH.equals(attrType)) {
expPlaceOfBirth = DirectoryString.getInstance(attrVal).getString();
} else if (ObjectIdentifiers.DN_GENDER.equals(attrType)) {
expGender = DERPrintableString.getInstance(attrVal).getString();
} else if (ObjectIdentifiers.DN_COUNTRY_OF_CITIZENSHIP.equals(attrType)) {
String country = DERPrintableString.getInstance(attrVal).getString();
expCountryOfCitizenshipList.add(country);
} else if (ObjectIdentifiers.DN_COUNTRY_OF_RESIDENCE.equals(attrType)) {
String country = DERPrintableString.getInstance(attrVal).getString();
expCountryOfResidenceList.add(country);
} else {
Set<ASN1Encodable> otherAttrVals = expOtherAttrs.get(attrType);
if (otherAttrVals == null) {
otherAttrVals = new HashSet<>();
expOtherAttrs.put(attrType, otherAttrVals);
}
otherAttrVals.add(attrVal);
}
}
SubjectDirectoryAttributes ext = SubjectDirectoryAttributes.getInstance(extensionValue);
Vector<?> subDirAttrs = ext.getAttributes();
ASN1GeneralizedTime dateOfBirth = null;
String placeOfBirth = null;
String gender = null;
Set<String> countryOfCitizenshipList = new HashSet<>();
Set<String> countryOfResidenceList = new HashSet<>();
Map<ASN1ObjectIdentifier, Set<ASN1Encodable>> otherAttrs = new HashMap<>();
List<ASN1ObjectIdentifier> attrTypes = new LinkedList<>(conf.getTypes());
final int n = subDirAttrs.size();
for (int i = 0; i < n; i++) {
Attribute attr = Attribute.getInstance(subDirAttrs.get(i));
ASN1ObjectIdentifier attrType = attr.getAttrType();
if ( !attrTypes.contains(attrType)) {
failureMsg.append("attribute of type " + attrType.getId()
+ " is present but not expected; ");
continue;
}
ASN1Encodable[] attrs = attr.getAttributeValues();
if (attrs.length != 1) {
failureMsg.append("attribute of type " + attrType.getId()
+ " does not single-value value: " + attrs.length + "; ");
continue;
}
ASN1Encodable attrVal = attrs[0];
if (ObjectIdentifiers.DN_DATE_OF_BIRTH.equals(attrType)) {
dateOfBirth = ASN1GeneralizedTime.getInstance(attrVal);
} else if (ObjectIdentifiers.DN_PLACE_OF_BIRTH.equals(attrType)) {
placeOfBirth = DirectoryString.getInstance(attrVal).getString();
} else if (ObjectIdentifiers.DN_GENDER.equals(attrType)) {
gender = DERPrintableString.getInstance(attrVal).getString();
} else if (ObjectIdentifiers.DN_COUNTRY_OF_CITIZENSHIP.equals(attrType)) {
String country = DERPrintableString.getInstance(attrVal).getString();
countryOfCitizenshipList.add(country);
} else if (ObjectIdentifiers.DN_COUNTRY_OF_RESIDENCE.equals(attrType)) {
String country = DERPrintableString.getInstance(attrVal).getString();
countryOfResidenceList.add(country);
} else {
Set<ASN1Encodable> otherAttrVals = otherAttrs.get(attrType);
if (otherAttrVals == null) {
otherAttrVals = new HashSet<>();
otherAttrs.put(attrType, otherAttrVals);
}
otherAttrVals.add(attrVal);
}
}
if (dateOfBirth != null) {
attrTypes.remove(ObjectIdentifiers.DN_DATE_OF_BIRTH);
}
if (placeOfBirth != null) {
attrTypes.remove(ObjectIdentifiers.DN_PLACE_OF_BIRTH);
}
if (gender != null) {
attrTypes.remove(ObjectIdentifiers.DN_GENDER);
}
if (!countryOfCitizenshipList.isEmpty()) {
attrTypes.remove(ObjectIdentifiers.DN_COUNTRY_OF_CITIZENSHIP);
}
if (!countryOfResidenceList.isEmpty()) {
attrTypes.remove(ObjectIdentifiers.DN_COUNTRY_OF_RESIDENCE);
}
attrTypes.removeAll(otherAttrs.keySet());
if (!attrTypes.isEmpty()) {
List<String> attrTypeTexts = new LinkedList<>();
for (ASN1ObjectIdentifier oid : attrTypes) {
attrTypeTexts.add(oid.getId());
}
failureMsg.append("required attributes of types " + attrTypeTexts
+ " are not present; ");
}
if (dateOfBirth != null) {
String timeStirng = dateOfBirth.getTimeString();
if (!SubjectDnSpec.PATTERN_DATE_OF_BIRTH.matcher(timeStirng).matches()) {
failureMsg.append("invalid dateOfBirth: " + timeStirng + "; ");
}
String exp = (expDateOfBirth == null) ? null : expDateOfBirth.getTimeString();
if (!timeStirng.equalsIgnoreCase(exp)) {
addViolation(failureMsg, "dateOfBirth", timeStirng, exp);
}
}
if (gender != null) {
if (!(gender.equalsIgnoreCase("F") || gender.equalsIgnoreCase("M"))) {
failureMsg.append("invalid gender: " + gender + "; ");
}
if (!gender.equalsIgnoreCase(expGender)) {
addViolation(failureMsg, "gender", gender, expGender);
}
}
if (placeOfBirth != null) {
if (!placeOfBirth.equals(expPlaceOfBirth)) {
addViolation(failureMsg, "placeOfBirth", placeOfBirth, expPlaceOfBirth);
}
}
if (!countryOfCitizenshipList.isEmpty()) {
Set<String> diffs = strInBnotInA(expCountryOfCitizenshipList, countryOfCitizenshipList);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("countryOfCitizenship ").append(diffs.toString());
failureMsg.append(" are present but not expected; ");
}
diffs = strInBnotInA(countryOfCitizenshipList, expCountryOfCitizenshipList);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("countryOfCitizenship ").append(diffs.toString());
failureMsg.append(" are absent but are required; ");
}
}
if (!countryOfResidenceList.isEmpty()) {
Set<String> diffs = strInBnotInA(expCountryOfResidenceList, countryOfResidenceList);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("countryOfResidence ").append(diffs.toString());
failureMsg.append(" are present but not expected; ");
}
diffs = strInBnotInA(countryOfResidenceList, expCountryOfResidenceList);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("countryOfResidence ").append(diffs.toString());
failureMsg.append(" are absent but are required; ");
}
}
if (!otherAttrs.isEmpty()) {
for (ASN1ObjectIdentifier attrType : otherAttrs.keySet()) {
Set<ASN1Encodable> expAttrValues = expOtherAttrs.get(attrType);
if (expAttrValues == null) {
failureMsg.append("attribute of type " + attrType.getId()
+ " is present but not requested; ");
continue;
}
Set<ASN1Encodable> attrValues = otherAttrs.get(attrType);
if (!attrValues.equals(expAttrValues)) {
failureMsg.append("attribute of type " + attrType.getId()
+ " differs from the requested one; ");
continue;
}
}
}
} // method checkExtensionSubjectDirectoryAttributes
private void checkExtensionSubjectAltName(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl, final X500Name requestedSubject) {
Set<GeneralNameMode> conf = certProfile.getSubjectAltNameModes();
GeneralName[] requested;
try {
requested = getRequestedSubjectAltNames(requestedSubject, requestedExtensions);
} catch (CertprofileException | BadCertTemplateException ex) {
String msg = "error while derive grantedSubject from requestedSubject";
LogUtil.warn(LOG, ex, msg);
failureMsg.append(msg);
return;
}
if (requested == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
GeneralName[] is = GeneralNames.getInstance(extensionValue).getNames();
GeneralName[] expected = new GeneralName[requested.length];
for (int i = 0; i < is.length; i++) {
try {
expected[i] = createGeneralName(is[i], conf);
} catch (BadCertTemplateException ex) {
failureMsg.append("could not process ").append(i + 1).append("-th name: ")
.append(ex.getMessage()).append("; ");
return;
}
}
if (is.length != expected.length) {
addViolation(failureMsg, "size of GeneralNames", is.length, expected.length);
return;
}
for (int i = 0; i < is.length; i++) {
if (!is[i].equals(expected[i])) {
failureMsg.append(i + 1).append("-th name does not match the requested one; ");
}
}
} // method checkExtensionSubjectAltName
private GeneralName[] getRequestedSubjectAltNames(final X500Name requestedSubject,
final Extensions requestedExtensions)
throws CertprofileException, BadCertTemplateException {
ASN1Encodable extValue = (requestedExtensions == null) ? null :
requestedExtensions.getExtensionParsedValue(Extension.subjectAlternativeName);
Map<ASN1ObjectIdentifier, GeneralNameTag> subjectToSubjectAltNameModes =
certProfile.getSubjectToSubjectAltNameModes();
if (extValue == null && subjectToSubjectAltNameModes == null) {
return null;
}
GeneralNames reqNames = (extValue == null) ? null : GeneralNames.getInstance(extValue);
Set<GeneralNameMode> subjectAltNameModes = certProfile.getSubjectAltNameModes();
if (subjectAltNameModes == null && subjectToSubjectAltNameModes == null) {
return (reqNames == null) ? null : reqNames.getNames();
}
List<GeneralName> grantedNames = new LinkedList<>();
// copy the required attributes of Subject
if (subjectToSubjectAltNameModes != null) {
X500Name grantedSubject;
try {
grantedSubject = certProfile.getSubject(requestedSubject).getGrantedSubject();
} catch (CertprofileException | BadCertTemplateException ex) {
if (certProfile.getSpecialCertprofileBehavior() == null) {
throw ex;
}
LogUtil.warn(LOG, ex, "could not derive granted subject from requested subject");
grantedSubject = requestedSubject;
}
for (ASN1ObjectIdentifier attrType : subjectToSubjectAltNameModes.keySet()) {
GeneralNameTag tag = subjectToSubjectAltNameModes.get(attrType);
RDN[] rdns = grantedSubject.getRDNs(attrType);
if (rdns == null) {
rdns = requestedSubject.getRDNs(attrType);
}
if (rdns == null) {
continue;
}
for (RDN rdn : rdns) {
String rdnValue = X509Util.rdnValueToString(rdn.getFirst().getValue());
switch (tag) {
case rfc822Name:
case dNSName:
case uniformResourceIdentifier:
case iPAddress:
case directoryName:
case registeredID:
grantedNames.add(new GeneralName(tag.getTag(), rdnValue));
break;
default:
throw new RuntimeException(
"should not reach here, unknown GeneralName tag " + tag);
} // end switch (tag)
}
}
}
// copy the requested SubjectAltName entries
if (reqNames != null) {
GeneralName[] reqL = reqNames.getNames();
for (int i = 0; i < reqL.length; i++) {
grantedNames.add(reqL[i]);
}
}
return grantedNames.isEmpty() ? null : grantedNames.toArray(new GeneralName[0]);
}
private void checkExtensionSubjectInfoAccess(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
Map<ASN1ObjectIdentifier, Set<GeneralNameMode>> conf =
certProfile.getSubjectInfoAccessModes();
if (conf == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
ASN1Encodable requestExtValue = null;
if (requestedExtensions != null) {
requestExtValue = requestedExtensions.getExtensionParsedValue(
Extension.subjectInfoAccess);
}
if (requestExtValue == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
ASN1Sequence requestSeq = ASN1Sequence.getInstance(requestExtValue);
ASN1Sequence certSeq = ASN1Sequence.getInstance(extensionValue);
int size = requestSeq.size();
if (certSeq.size() != size) {
addViolation(failureMsg, "size of GeneralNames", certSeq.size(), size);
return;
}
for (int i = 0; i < size; i++) {
AccessDescription ad = AccessDescription.getInstance(requestSeq.getObjectAt(i));
ASN1ObjectIdentifier accessMethod = ad.getAccessMethod();
Set<GeneralNameMode> generalNameModes = conf.get(accessMethod);
if (generalNameModes == null) {
failureMsg.append("accessMethod in requestedExtension ");
failureMsg.append(accessMethod.getId()).append(" is not allowed; ");
continue;
}
AccessDescription certAccessDesc = AccessDescription.getInstance(
certSeq.getObjectAt(i));
ASN1ObjectIdentifier certAccessMethod = certAccessDesc.getAccessMethod();
boolean bo = (accessMethod == null) ? (certAccessMethod == null)
: accessMethod.equals(certAccessMethod);
if (!bo) {
addViolation(failureMsg, "accessMethod",
(certAccessMethod == null) ? "null" : certAccessMethod.getId(),
(accessMethod == null) ? "null" : accessMethod.getId());
continue;
}
GeneralName accessLocation;
try {
accessLocation = createGeneralName(ad.getAccessLocation(), generalNameModes);
} catch (BadCertTemplateException ex) {
failureMsg.append("invalid requestedExtension: ").append(ex.getMessage());
failureMsg.append("; ");
continue;
}
GeneralName certAccessLocation = certAccessDesc.getAccessLocation();
if (!certAccessLocation.equals(accessLocation)) {
failureMsg.append("accessLocation does not match the requested one; ");
}
}
} // method checkExtensionSubjectInfoAccess
private void checkExtensionIssuerAltNames(final StringBuilder failureMsg,
final byte[] extensionValue, final X509IssuerInfo issuerInfo) {
Extension caSubjectAltExtension = issuerInfo.getBcCert().getTBSCertificate().getExtensions()
.getExtension(Extension.subjectAlternativeName);
if (caSubjectAltExtension == null) {
failureMsg.append("issuerAlternativeName is present but expected 'none'; ");
return;
}
byte[] caSubjectAltExtensionValue = caSubjectAltExtension.getExtnValue().getOctets();
if (!Arrays.equals(caSubjectAltExtensionValue, extensionValue)) {
addViolation(failureMsg, "issuerAltNames", hex(extensionValue),
hex(caSubjectAltExtensionValue));
}
} // method checkExtensionIssuerAltNames
private void checkExtensionCrlDistributionPoints(final StringBuilder failureMsg,
final byte[] extensionValue, final X509IssuerInfo issuerInfo) {
CRLDistPoint isCrlDistPoints = CRLDistPoint.getInstance(extensionValue);
DistributionPoint[] isDistributionPoints = isCrlDistPoints.getDistributionPoints();
if (isDistributionPoints == null) {
addViolation(failureMsg, "size of CRLDistributionPoints", 0, 1);
return;
} else {
int len = isDistributionPoints.length;
if (len != 1) {
addViolation(failureMsg, "size of CRLDistributionPoints", len, 1);
return;
}
}
Set<String> isCrlUrls = new HashSet<>();
for (DistributionPoint entry : isDistributionPoints) {
int asn1Type = entry.getDistributionPoint().getType();
if (asn1Type != DistributionPointName.FULL_NAME) {
addViolation(failureMsg, "tag of DistributionPointName of CRLDistibutionPoints",
asn1Type, DistributionPointName.FULL_NAME);
continue;
}
GeneralNames isDistributionPointNames =
GeneralNames.getInstance(entry.getDistributionPoint().getName());
GeneralName[] names = isDistributionPointNames.getNames();
for (int i = 0; i < names.length; i++) {
GeneralName name = names[i];
if (name.getTagNo() != GeneralName.uniformResourceIdentifier) {
addViolation(failureMsg, "tag of CRL URL", name.getTagNo(),
GeneralName.uniformResourceIdentifier);
} else {
String uri = ((ASN1String) name.getName()).getString();
isCrlUrls.add(uri);
}
}
Set<String> expCrlUrls = issuerInfo.getCrlUrls();
Set<String> diffs = strInBnotInA(expCrlUrls, isCrlUrls);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("CRL URLs ").append(diffs.toString())
.append(" are present but not expected; ");
}
diffs = strInBnotInA(isCrlUrls, expCrlUrls);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("CRL URLs ").append(diffs.toString())
.append(" are absent but are required; ");
}
}
} // method checkExtensionCrlDistributionPoints
private void checkExtensionDeltaCrlDistributionPoints(final StringBuilder failureMsg,
final byte[] extensionValue, final X509IssuerInfo issuerInfo) {
CRLDistPoint isCrlDistPoints = CRLDistPoint.getInstance(extensionValue);
DistributionPoint[] isDistributionPoints = isCrlDistPoints.getDistributionPoints();
if (isDistributionPoints == null) {
addViolation(failureMsg, "size of CRLDistributionPoints (deltaCRL)", 0, 1);
return;
} else {
int len = isDistributionPoints.length;
if (len != 1) {
addViolation(failureMsg, "size of CRLDistributionPoints (deltaCRL)", len, 1);
return;
}
}
Set<String> isCrlUrls = new HashSet<>();
for (DistributionPoint entry : isDistributionPoints) {
int asn1Type = entry.getDistributionPoint().getType();
if (asn1Type != DistributionPointName.FULL_NAME) {
addViolation(failureMsg,
"tag of DistributionPointName of CRLDistibutionPoints (deltaCRL)",
asn1Type, DistributionPointName.FULL_NAME);
continue;
}
GeneralNames isDistributionPointNames =
GeneralNames.getInstance(entry.getDistributionPoint().getName());
GeneralName[] names = isDistributionPointNames.getNames();
for (int i = 0; i < names.length; i++) {
GeneralName name = names[i];
if (name.getTagNo() != GeneralName.uniformResourceIdentifier) {
addViolation(failureMsg, "tag of deltaCRL URL", name.getTagNo(),
GeneralName.uniformResourceIdentifier);
} else {
String uri = ((ASN1String) name.getName()).getString();
isCrlUrls.add(uri);
}
}
Set<String> expCrlUrls = issuerInfo.getCrlUrls();
Set<String> diffs = strInBnotInA(expCrlUrls, isCrlUrls);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("deltaCRL URLs ").append(diffs.toString())
.append(" are present but not expected; ");
}
diffs = strInBnotInA(isCrlUrls, expCrlUrls);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("deltaCRL URLs ").append(diffs.toString())
.append(" are absent but are required; ");
}
}
} // method checkExtensionDeltaCrlDistributionPoints
private void checkExtensionAdmission(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
AdmissionSyntaxOption conf = certProfile.getAdmission();
ASN1ObjectIdentifier type = ObjectIdentifiers.id_extension_admission;
if (conf == null) {
byte[] expected = getExpectedExtValue(type, requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension value", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
List<List<String>> reqRegNumsList = null;
if (requestedExtensions != null && conf.isInputFromRequestRequired()) {
Extension extension = requestedExtensions.getExtension(type);
if (extension == null) {
failureMsg.append("no Admission extension is contained in the request;");
return;
}
Admissions[] reqAdmissions =
org.bouncycastle.asn1.isismtt.x509.AdmissionSyntax.getInstance(
extension.getParsedValue()).getContentsOfAdmissions();
final int n = reqAdmissions.length;
reqRegNumsList = new ArrayList<>(n);
for (int i = 0; i < n; i++) {
Admissions reqAdmission = reqAdmissions[i];
ProfessionInfo[] reqPis = reqAdmission.getProfessionInfos();
List<String> reqNums = new ArrayList<>(reqPis.length);
reqRegNumsList.add(reqNums);
for (ProfessionInfo reqPi : reqPis) {
String reqNum = reqPi.getRegistrationNumber();
reqNums.add(reqNum);
}
}
}
try {
byte[] expected = conf.getExtensionValue(reqRegNumsList).getValue()
.toASN1Primitive().getEncoded();
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension valus", hex(extensionValue), hex(expected));
}
} catch (IOException ex) {
LogUtil.error(LOG, ex);
failureMsg.append("IOException while computing the expected extension value;");
return;
} catch (BadCertTemplateException ex) {
LogUtil.error(LOG, ex);
failureMsg.append(
"BadCertTemplateException while computing the expected extension value;");
}
} // method checkExtensionAdmission
private void checkExtensionAuthorityInfoAccess(final StringBuilder failureMsg,
final byte[] extensionValue, final X509IssuerInfo issuerInfo) {
AuthorityInfoAccessControl aiaControl = certProfile.getAiaControl();
Set<String> expCaIssuerUris = (aiaControl == null || aiaControl.includesCaIssuers())
? issuerInfo.getCaIssuerUrls() : Collections.emptySet();
Set<String> expOcspUris = (aiaControl == null || aiaControl.includesOcsp())
? issuerInfo.getOcspUrls() : Collections.emptySet();
if (CollectionUtil.isEmpty(expCaIssuerUris) && CollectionUtil.isEmpty(expOcspUris)) {
failureMsg.append("AIA is present but expected is 'none'; ");
return;
}
AuthorityInformationAccess isAia = AuthorityInformationAccess.getInstance(extensionValue);
checkAia(failureMsg, isAia, X509ObjectIdentifiers.id_ad_caIssuers, expCaIssuerUris);
checkAia(failureMsg, isAia, X509ObjectIdentifiers.id_ad_ocsp, expOcspUris);
} // method checkExtensionAuthorityInfoAccess
private void checkExtensionOcspNocheck(final StringBuilder failureMsg,
final byte[] extensionValue) {
if (!Arrays.equals(DER_NULL, extensionValue)) {
failureMsg.append("value is not DER NULL; ");
}
}
private void checkExtensionRestriction(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
checkDirectoryString(ObjectIdentifiers.id_extension_restriction, restriction,
failureMsg, extensionValue, requestedExtensions, extControl);
}
private void checkExtensionAdditionalInformation(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
checkDirectoryString(ObjectIdentifiers.id_extension_additionalInformation,
additionalInformation, failureMsg, extensionValue, requestedExtensions, extControl);
}
private void checkDirectoryString(final ASN1ObjectIdentifier extType,
final QaDirectoryString conf, final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
if (conf == null) {
byte[] expected = getExpectedExtValue(extType,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
ASN1Primitive asn1;
try {
asn1 = ASN1Primitive.fromByteArray(extensionValue);
} catch (IOException ex) {
failureMsg.append("invalid syntax of extension value; ");
return;
}
boolean correctStringType;
switch (conf.getType()) {
case bmpString:
correctStringType = (asn1 instanceof DERBMPString);
break;
case printableString:
correctStringType = (asn1 instanceof DERPrintableString);
break;
case teletexString:
correctStringType = (asn1 instanceof DERT61String);
break;
case utf8String:
correctStringType = (asn1 instanceof DERUTF8String);
break;
default:
throw new RuntimeException("should not reach here, unknown DirectoryStringType "
+ conf.getType());
} // end switch
if (!correctStringType) {
failureMsg.append("extension value is not of type DirectoryString.")
.append(conf.getText()).append("; ");
return;
}
String extTextValue = ((ASN1String) asn1).getString();
if (!conf.getText().equals(extTextValue)) {
addViolation(failureMsg, "content", extTextValue, conf.getText());
}
} // method checkDirectoryString
private void checkExtensionValidityModel(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
ASN1ObjectIdentifier conf = validityModelId;
if (conf == null) {
byte[] expected = getExpectedExtValue(ObjectIdentifiers.id_extension_validityModel,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
ASN1ObjectIdentifier extValue = ASN1ObjectIdentifier.getInstance(extensionValue);
if (!conf.equals(extValue)) {
addViolation(failureMsg, "content", extValue, conf);
}
} // method checkExtensionValidityModel
private void checkExtensionPrivateKeyUsagePeriod(final StringBuilder failureMsg,
final byte[] extensionValue, final Date certNotBefore, final Date certNotAfter) {
ASN1GeneralizedTime notBefore = new ASN1GeneralizedTime(certNotBefore);
Date dateNotAfter;
CertValidity privateKeyUsagePeriod = certProfile.getPrivateKeyUsagePeriod();
if (privateKeyUsagePeriod == null) {
dateNotAfter = certNotAfter;
} else {
dateNotAfter = privateKeyUsagePeriod.add(certNotBefore);
if (dateNotAfter.after(certNotAfter)) {
dateNotAfter = certNotAfter;
}
}
ASN1GeneralizedTime notAfter = new ASN1GeneralizedTime(dateNotAfter);
org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod extValue =
org.bouncycastle.asn1.x509.PrivateKeyUsagePeriod.getInstance(extensionValue);
ASN1GeneralizedTime time = extValue.getNotBefore();
if (time == null) {
failureMsg.append("notBefore is absent but expected present; ");
} else if (!time.equals(notBefore)) {
addViolation(failureMsg, "notBefore", time.getTimeString(), notBefore.getTimeString());
}
time = extValue.getNotAfter();
if (time == null) {
failureMsg.append("notAfter is absent but expected present; ");
} else if (!time.equals(notAfter)) {
addViolation(failureMsg, "notAfter", time.getTimeString(), notAfter.getTimeString());
}
} // method checkExtensionPrivateKeyUsagePeriod
private void checkExtensionQcStatements(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QcStatements conf = qcStatements;
if (conf == null) {
byte[] expected = getExpectedExtValue(Extension.qCStatements,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", extensionValue,
(expected == null) ? "not present" : hex(expected));
}
return;
}
final int expSize = conf.getQcStatement().size();
ASN1Sequence extValue = ASN1Sequence.getInstance(extensionValue);
final int isSize = extValue.size();
if (isSize != expSize) {
addViolation(failureMsg, "number of statements", isSize, expSize);
return;
}
// extract the euLimit and pdsLocations data from request
Map<String, int[]> reqQcEuLimits = new HashMap<>();
Extension reqExtension = (requestedExtensions == null) ? null :
requestedExtensions.getExtension(Extension.qCStatements);
if (reqExtension != null) {
ASN1Sequence seq = ASN1Sequence.getInstance(reqExtension.getParsedValue());
final int n = seq.size();
for (int j = 0; j < n; j++) {
QCStatement stmt = QCStatement.getInstance(seq.getObjectAt(j));
if (ObjectIdentifiers.id_etsi_qcs_QcLimitValue.equals(stmt.getStatementId())) {
MonetaryValue monetaryValue = MonetaryValue.getInstance(
stmt.getStatementInfo());
int amount = monetaryValue.getAmount().intValue();
int exponent = monetaryValue.getExponent().intValue();
Iso4217CurrencyCode currency = monetaryValue.getCurrency();
String currencyS = currency.isAlphabetic()
? currency.getAlphabetic().toUpperCase()
: Integer.toString(currency.getNumeric());
reqQcEuLimits.put(currencyS, new int[]{amount, exponent});
}
}
}
for (int i = 0; i < expSize; i++) {
QCStatement is = QCStatement.getInstance(extValue.getObjectAt(i));
QcStatementType exp = conf.getQcStatement().get(i);
if (!is.getStatementId().getId().equals(exp.getStatementId().getValue())) {
addViolation(failureMsg, "statmentId[" + i + "]",
is.getStatementId().getId(), exp.getStatementId().getValue());
continue;
}
if (exp.getStatementValue() == null) {
if (is.getStatementInfo() != null) {
addViolation(failureMsg, "statmentInfo[" + i + "]", "present", "absent");
}
continue;
}
if (is.getStatementInfo() == null) {
addViolation(failureMsg, "statmentInfo[" + i + "]", "absent", "present");
continue;
}
QcStatementValueType expStatementValue = exp.getStatementValue();
try {
if (expStatementValue.getConstant() != null) {
byte[] expValue = expStatementValue.getConstant().getValue();
byte[] isValue = is.getStatementInfo().toASN1Primitive().getEncoded();
if (!Arrays.equals(isValue, expValue)) {
addViolation(failureMsg, "statementInfo[" + i + "]",
hex(isValue), hex(expValue));
}
} else if (expStatementValue.getQcRetentionPeriod() != null) {
String isValue = ASN1Integer.getInstance(is.getStatementInfo()).toString();
String expValue = expStatementValue.getQcRetentionPeriod().toString();
if (!isValue.equals(expValue)) {
addViolation(failureMsg, "statementInfo[" + i + "]", isValue, expValue);
}
} else if (expStatementValue.getPdsLocations() != null) {
Set<String> pdsLocations = new HashSet<>();
ASN1Sequence pdsLocsSeq = ASN1Sequence.getInstance(is.getStatementInfo());
int size = pdsLocsSeq.size();
for (int k = 0; k < size; k++) {
ASN1Sequence pdsLocSeq = ASN1Sequence.getInstance(
pdsLocsSeq.getObjectAt(k));
int size2 = pdsLocSeq.size();
if (size2 != 2) {
throw new IllegalArgumentException("sequence size is " + size2
+ " but expected 2");
}
String url = DERIA5String.getInstance(pdsLocSeq.getObjectAt(0)).getString();
String lang = DERPrintableString.getInstance(pdsLocSeq.getObjectAt(1))
.getString();
pdsLocations.add("url=" + url + ",lang=" + lang);
}
PdsLocationsType pdsLocationsConf = expStatementValue.getPdsLocations();
Set<String> expectedPdsLocations = new HashSet<>();
for (PdsLocationType m : pdsLocationsConf.getPdsLocation()) {
expectedPdsLocations.add("url=" + m.getUrl() + ",lang=" + m.getLanguage());
}
Set<String> diffs = strInBnotInA(expectedPdsLocations, pdsLocations);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("statementInfo[" + i + "]: ").append(diffs.toString());
failureMsg.append(" are present but not expected; ");
}
diffs = strInBnotInA(pdsLocations, expectedPdsLocations);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append("statementInfo[" + i + "]: ").append(diffs.toString());
failureMsg.append(" are absent but are required; ");
}
} else if (expStatementValue.getQcEuLimitValue() != null) {
QcEuLimitValueType euLimitConf = expStatementValue.getQcEuLimitValue();
String expCurrency = euLimitConf.getCurrency().toUpperCase();
int[] expAmountExp = reqQcEuLimits.get(expCurrency);
Range2Type range = euLimitConf.getAmount();
int value;
if (range.getMin() == range.getMax()) {
value = range.getMin();
} else if (expAmountExp != null) {
value = expAmountExp[0];
} else {
failureMsg.append("found no QcEuLimit for currency '").append(expCurrency)
.append("'; ");
return;
}
// CHECKSTYLE:SKIP
String expAmount = Integer.toString(value);
range = euLimitConf.getExponent();
if (range.getMin() == range.getMax()) {
value = range.getMin();
} else if (expAmountExp != null) {
value = expAmountExp[1];
} else {
failureMsg.append("found no QcEuLimit for currency '").append(expCurrency)
.append("'; ");
return;
}
String expExponent = Integer.toString(value);
MonetaryValue monterayValue = MonetaryValue.getInstance(is.getStatementInfo());
Iso4217CurrencyCode currency = monterayValue.getCurrency();
String isCurrency = currency.isAlphabetic() ? currency.getAlphabetic()
: Integer.toString(currency.getNumeric());
String isAmount = monterayValue.getAmount().toString();
String isExponent = monterayValue.getExponent().toString();
if (!isCurrency.equals(expCurrency)) {
addViolation(failureMsg, "statementInfo[" + i + "].qcEuLimit.currency",
isCurrency, expCurrency);
}
if (!isAmount.equals(expAmount)) {
addViolation(failureMsg, "statementInfo[" + i + "].qcEuLimit.amount",
isAmount, expAmount);
}
if (!isExponent.equals(expExponent)) {
addViolation(failureMsg, "statementInfo[" + i + "].qcEuLimit.exponent",
isExponent, expExponent);
}
} else {
throw new RuntimeException("statementInfo[" + i + "]should not reach here");
}
} catch (IOException ex) {
failureMsg.append("statementInfo[").append(i).append("] has incorrect syntax; ");
}
}
} // method checkExtensionQcStatements
private void checkExtensionBiometricInfo(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
BiometricInfoOption conf = certProfile.getBiometricInfo();
if (conf == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
ASN1Encodable extInRequest = null;
if (requestedExtensions != null) {
extInRequest = requestedExtensions.getExtensionParsedValue(Extension.biometricInfo);
}
if (extInRequest == null) {
failureMsg.append("extension is present but not expected; ");
return;
}
ASN1Sequence extValueInReq = ASN1Sequence.getInstance(extInRequest);
final int expSize = extValueInReq.size();
ASN1Sequence extValue = ASN1Sequence.getInstance(extensionValue);
final int isSize = extValue.size();
if (isSize != expSize) {
addViolation(failureMsg, "number of biometricData", isSize, expSize);
return;
}
for (int i = 0; i < expSize; i++) {
BiometricData isData = BiometricData.getInstance(extValue.getObjectAt(i));
BiometricData expData = BiometricData.getInstance(extValueInReq.getObjectAt(i));
TypeOfBiometricData isType = isData.getTypeOfBiometricData();
TypeOfBiometricData expType = expData.getTypeOfBiometricData();
if (!isType.equals(expType)) {
String isStr = isType.isPredefined()
? Integer.toString(isType.getPredefinedBiometricType())
: isType.getBiometricDataOid().getId();
String expStr = expType.isPredefined()
? Integer.toString(expType.getPredefinedBiometricType())
: expType.getBiometricDataOid().getId();
addViolation(failureMsg, "biometricData[" + i + "].typeOfBiometricData",
isStr, expStr);
}
ASN1ObjectIdentifier is = isData.getHashAlgorithm().getAlgorithm();
ASN1ObjectIdentifier exp = expData.getHashAlgorithm().getAlgorithm();
if (!is.equals(exp)) {
addViolation(failureMsg, "biometricData[" + i + "].hashAlgorithm",
is.getId(), exp.getId());
}
ASN1Encodable isHashAlgoParam = isData.getHashAlgorithm().getParameters();
if (isHashAlgoParam == null) {
failureMsg.append("biometricData[").append(i)
.append("].hashAlgorithm.parameters is 'present'");
failureMsg.append(" but expected 'absent'; ");
} else {
try {
byte[] isBytes = isHashAlgoParam.toASN1Primitive().getEncoded();
if (!Arrays.equals(isBytes, DER_NULL)) {
addViolation(failureMsg,
"biometricData[" + i + "].biometricDataHash.parameters",
hex(isBytes), hex(DER_NULL));
}
} catch (IOException ex) {
failureMsg.append("biometricData[").append(i)
.append("].biometricDataHash.parameters has incorrect syntax; ");
}
}
byte[] isBytes = isData.getBiometricDataHash().getOctets();
byte[] expBytes = expData.getBiometricDataHash().getOctets();
if (!Arrays.equals(isBytes, expBytes)) {
addViolation(failureMsg, "biometricData[" + i + "].biometricDataHash",
hex(isBytes), hex(expBytes));
}
DERIA5String str = isData.getSourceDataUri();
String isSourceDataUri = (str == null) ? null : str.getString();
String expSourceDataUri = null;
if (conf.getSourceDataUriOccurrence() != TripleState.FORBIDDEN) {
str = expData.getSourceDataUri();
expSourceDataUri = (str == null) ? null : str.getString();
}
if (expSourceDataUri == null) {
if (isSourceDataUri != null) {
addViolation(failureMsg, "biometricData[" + i + "].sourceDataUri",
"present", "absent");
}
} else {
if (isSourceDataUri == null) {
failureMsg.append("biometricData[").append(i)
.append("].sourceDataUri is 'absent'");
failureMsg.append(" but expected 'present'; ");
} else if (!isSourceDataUri.equals(expSourceDataUri)) {
addViolation(failureMsg, "biometricData[" + i + "].sourceDataUri",
isSourceDataUri, expSourceDataUri);
}
}
}
} // method checkExtensionBiometricInfo
private void checkExtensionAuthorizationTemplate(final StringBuilder failureMsg,
final byte[] extensionValue, final Extensions requestedExtensions,
final ExtensionControl extControl) {
QaAuthorizationTemplate conf = authorizationTemplate;
if (conf == null) {
byte[] expected = getExpectedExtValue(
ObjectIdentifiers.id_xipki_ext_authorizationTemplate,
requestedExtensions, extControl);
if (!Arrays.equals(expected, extensionValue)) {
addViolation(failureMsg, "extension values", hex(extensionValue),
(expected == null) ? "not present" : hex(expected));
}
return;
}
ASN1Sequence seq = ASN1Sequence.getInstance(extensionValue);
ASN1ObjectIdentifier type = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
ASN1OctetString accessRights = DEROctetString.getInstance(seq.getObjectAt(1));
if (!conf.getType().equals(type.getId())) {
addViolation(failureMsg, "type", type.getId(), conf.getType());
}
byte[] isRights = accessRights.getOctets();
if (!Arrays.equals(conf.getAccessRights(), isRights)) {
addViolation(failureMsg, "accessRights", hex(isRights), hex(conf.getAccessRights()));
}
} // method checkExtensionAuthorizationTemplate
private Set<KeyUsageControl> getKeyusage(final boolean required) {
Set<KeyUsageControl> ret = new HashSet<>();
Set<KeyUsageControl> controls = certProfile.getKeyusages();
if (controls != null) {
for (KeyUsageControl control : controls) {
if (control.isRequired() == required) {
ret.add(control);
}
}
}
return ret;
}
private Set<ExtKeyUsageControl> getExtKeyusage(final boolean required) {
Set<ExtKeyUsageControl> ret = new HashSet<>();
Set<ExtKeyUsageControl> controls = certProfile.getExtendedKeyusages();
if (controls != null) {
for (ExtKeyUsageControl control : controls) {
if (control.isRequired() == required) {
ret.add(control);
}
}
}
return ret;
}
private byte[] getConstantExtensionValue(final ASN1ObjectIdentifier type) {
return (constantExtensions == null) ? null : constantExtensions.get(type).getValue();
}
private Object getExtensionValue(final ASN1ObjectIdentifier type,
final ExtensionsType extensionsType, final Class<?> expectedClass)
throws CertprofileException {
for (ExtensionType m : extensionsType.getExtension()) {
if (!m.getType().getValue().equals(type.getId())) {
continue;
}
if (m.getValue() == null || m.getValue().getAny() == null) {
return null;
}
Object obj = m.getValue().getAny();
if (expectedClass.isAssignableFrom(obj.getClass())) {
return obj;
} else if (ConstantExtValue.class.isAssignableFrom(obj.getClass())) {
// will be processed later
return null;
} else {
String displayName = ObjectIdentifiers.oidToDisplayName(type);
throw new CertprofileException("the extension configuration for " + displayName
+ " is not of the expected type " + expectedClass.getName());
}
}
throw new RuntimeException("should not reach here: undefined extension "
+ ObjectIdentifiers.oidToDisplayName(type));
} // method getExtensionValue
public static Map<ASN1ObjectIdentifier, QaExtensionValue> buildConstantExtesions(
final ExtensionsType extensionsType) throws CertprofileException {
if (extensionsType == null) {
return null;
}
Map<ASN1ObjectIdentifier, QaExtensionValue> map = new HashMap<>();
for (ExtensionType m : extensionsType.getExtension()) {
if (m.getValue() == null || !(m.getValue().getAny() instanceof ConstantExtValue)) {
continue;
}
ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(m.getType().getValue());
if (Extension.subjectAlternativeName.equals(oid)
|| Extension.subjectInfoAccess.equals(oid)
|| Extension.biometricInfo.equals(oid)) {
continue;
}
ConstantExtValue extConf = (ConstantExtValue) m.getValue().getAny();
byte[] encodedValue = extConf.getValue();
ASN1StreamParser parser = new ASN1StreamParser(encodedValue);
try {
parser.readObject();
} catch (IOException ex) {
throw new CertprofileException("could not parse the constant extension value", ex);
}
QaExtensionValue extension = new QaExtensionValue(m.isCritical(), encodedValue);
map.put(oid, extension);
}
if (CollectionUtil.isEmpty(map)) {
return null;
}
return Collections.unmodifiableMap(map);
} // method buildConstantExtesions
private static ASN1Encodable readAsn1Encodable(final byte[] encoded)
throws CertprofileException {
ASN1StreamParser parser = new ASN1StreamParser(encoded);
try {
return parser.readObject();
} catch (IOException ex) {
throw new CertprofileException("could not parse the constant extension value", ex);
}
}
private static String hex(final byte[] bytes) {
return Hex.toHexString(bytes);
}
private static Set<String> strInBnotInA(final Collection<String> collectionA,
final Collection<String> collectionB) {
if (collectionB == null) {
return Collections.emptySet();
}
Set<String> result = new HashSet<>();
for (String entry : collectionB) {
if (collectionA == null || !collectionA.contains(entry)) {
result.add(entry);
}
}
return result;
}
static Set<Range> buildParametersMap(final RangesType ranges) {
if (ranges == null) {
return null;
}
Set<Range> ret = new HashSet<>();
for (RangeType range : ranges.getRange()) {
if (range.getMin() != null || range.getMax() != null) {
ret.add(new Range(range.getMin(), range.getMax()));
}
}
return ret;
}
private static GeneralName createGeneralName(final GeneralName reqName,
final Set<GeneralNameMode> modes) throws BadCertTemplateException {
int tag = reqName.getTagNo();
GeneralNameMode mode = null;
if (modes != null) {
for (GeneralNameMode m : modes) {
if (m.getTag().getTag() == tag) {
mode = m;
break;
}
}
if (mode == null) {
throw new BadCertTemplateException("generalName tag " + tag + " is not allowed");
}
}
switch (tag) {
case GeneralName.rfc822Name:
case GeneralName.dNSName:
case GeneralName.uniformResourceIdentifier:
case GeneralName.iPAddress:
case GeneralName.registeredID:
case GeneralName.directoryName:
return new GeneralName(tag, reqName.getName());
case GeneralName.otherName:
ASN1Sequence reqSeq = ASN1Sequence.getInstance(reqName.getName());
ASN1ObjectIdentifier type = ASN1ObjectIdentifier.getInstance(reqSeq.getObjectAt(0));
if (mode != null && !mode.getAllowedTypes().contains(type)) {
throw new BadCertTemplateException(
"otherName.type " + type.getId() + " is not allowed");
}
ASN1Encodable value = ASN1TaggedObject.getInstance(reqSeq.getObjectAt(1)).getObject();
String text;
if (!(value instanceof ASN1String)) {
throw new BadCertTemplateException("otherName.value is not a String");
} else {
text = ((ASN1String) value).getString();
}
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(type);
vector.add(new DERTaggedObject(true, 0, new DERUTF8String(text)));
DERSequence seq = new DERSequence(vector);
return new GeneralName(GeneralName.otherName, seq);
case GeneralName.ediPartyName:
reqSeq = ASN1Sequence.getInstance(reqName.getName());
int size = reqSeq.size();
String nameAssigner = null;
int idx = 0;
if (size > 1) {
DirectoryString ds = DirectoryString.getInstance(
ASN1TaggedObject.getInstance(reqSeq.getObjectAt(idx++)).getObject());
nameAssigner = ds.getString();
}
DirectoryString ds = DirectoryString.getInstance(
ASN1TaggedObject.getInstance(reqSeq.getObjectAt(idx++)).getObject());
String partyName = ds.getString();
vector = new ASN1EncodableVector();
if (nameAssigner != null) {
vector.add(new DERTaggedObject(false, 0, new DirectoryString(nameAssigner)));
}
vector.add(new DERTaggedObject(false, 1, new DirectoryString(partyName)));
seq = new DERSequence(vector);
return new GeneralName(GeneralName.ediPartyName, seq);
default:
throw new RuntimeException("should not reach here, unknown GeneralName tag " + tag);
} // end switch
} // method createGeneralName
private static Set<String> getKeyUsage(final byte[] extensionValue) {
Set<String> usages = new HashSet<>();
org.bouncycastle.asn1.x509.KeyUsage reqKeyUsage =
org.bouncycastle.asn1.x509.KeyUsage.getInstance(extensionValue);
for (KeyUsage k : KeyUsage.values()) {
if (reqKeyUsage.hasUsages(k.getBcUsage())) {
usages.add(k.getName());
}
}
return usages;
}
private static Set<String> getExtKeyUsage(final byte[] extensionValue) {
Set<String> usages = new HashSet<>();
org.bouncycastle.asn1.x509.ExtendedKeyUsage reqKeyUsage =
org.bouncycastle.asn1.x509.ExtendedKeyUsage.getInstance(extensionValue);
for (KeyPurposeId usage : reqKeyUsage.getUsages()) {
usages.add(usage.getId());
}
return usages;
}
private static void checkAia(final StringBuilder failureMsg,
final AuthorityInformationAccess aia, final ASN1ObjectIdentifier accessMethod,
final Set<String> expectedUris) {
String typeDesc;
if (X509ObjectIdentifiers.id_ad_ocsp.equals(accessMethod)) {
typeDesc = "OCSP";
} else if (X509ObjectIdentifiers.id_ad_caIssuers.equals(accessMethod)) {
typeDesc = "caIssuer";
} else {
typeDesc = accessMethod.getId();
}
List<AccessDescription> isAccessDescriptions = new LinkedList<>();
for (AccessDescription accessDescription : aia.getAccessDescriptions()) {
if (accessMethod.equals(accessDescription.getAccessMethod())) {
isAccessDescriptions.add(accessDescription);
}
}
int size = isAccessDescriptions.size();
if (size != expectedUris.size()) {
addViolation(failureMsg, "number of AIA " + typeDesc + " URIs",
size, expectedUris.size());
return;
}
Set<String> isUris = new HashSet<>();
for (int i = 0; i < size; i++) {
GeneralName isAccessLocation = isAccessDescriptions.get(i).getAccessLocation();
if (isAccessLocation.getTagNo() != GeneralName.uniformResourceIdentifier) {
addViolation(failureMsg, "tag of accessLocation of AIA ",
isAccessLocation.getTagNo(), GeneralName.uniformResourceIdentifier);
} else {
String isOcspUri = ((ASN1String) isAccessLocation.getName()).getString();
isUris.add(isOcspUri);
}
}
Set<String> diffs = strInBnotInA(expectedUris, isUris);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append(typeDesc).append(" URIs ").append(diffs.toString());
failureMsg.append(" are present but not expected; ");
}
diffs = strInBnotInA(isUris, expectedUris);
if (CollectionUtil.isNonEmpty(diffs)) {
failureMsg.append(typeDesc).append(" URIs ").append(diffs.toString());
failureMsg.append(" are absent but are required; ");
}
} // method checkAia
private static void addViolation(final StringBuilder failureMsg, final String field,
final Object is, final Object expected) {
failureMsg.append(field).append(" is '").append(is);
failureMsg.append("' but expected '").append(expected).append("';");
}
}