org.xipki.pki.ca.certprofile.XmlX509CertprofileUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.xipki.pki.ca.certprofile.XmlX509CertprofileUtil.java

Source

/*
 *
 * Copyright (c) 2013 - 2016 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.certprofile;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
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 javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.validation.SchemaFactory;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1StreamParser;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.DERTaggedObject;
import org.bouncycastle.asn1.isismtt.x509.NamingAuthority;
import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
import org.bouncycastle.asn1.x500.DirectoryString;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x509.CertPolicyId;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralSubtree;
import org.bouncycastle.asn1.x509.NameConstraints;
import org.bouncycastle.asn1.x509.PolicyInformation;
import org.bouncycastle.asn1.x509.PolicyMappings;
import org.bouncycastle.asn1.x509.PolicyQualifierInfo;
import org.bouncycastle.asn1.x509.UserNotice;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xipki.commons.common.util.CollectionUtil;
import org.xipki.commons.common.util.ParamUtil;
import org.xipki.commons.common.util.StringUtil;
import org.xipki.commons.common.util.XmlUtil;
import org.xipki.commons.security.KeyUsage;
import org.xipki.commons.security.util.X509Util;
import org.xipki.pki.ca.api.profile.CertprofileException;
import org.xipki.pki.ca.api.profile.DirectoryStringType;
import org.xipki.pki.ca.api.profile.ExtensionControl;
import org.xipki.pki.ca.api.profile.ExtensionValue;
import org.xipki.pki.ca.api.profile.GeneralNameMode;
import org.xipki.pki.ca.api.profile.GeneralNameTag;
import org.xipki.pki.ca.api.profile.KeyParametersOption;
import org.xipki.pki.ca.api.profile.Range;
import org.xipki.pki.ca.api.profile.StringType;
import org.xipki.pki.ca.api.profile.x509.CertificatePolicyInformation;
import org.xipki.pki.ca.api.profile.x509.CertificatePolicyQualifier;
import org.xipki.pki.ca.api.profile.x509.ExtKeyUsageControl;
import org.xipki.pki.ca.api.profile.x509.KeyUsageControl;
import org.xipki.pki.ca.certprofile.commonpki.AdmissionSyntaxOption;
import org.xipki.pki.ca.certprofile.commonpki.AdmissionsOption;
import org.xipki.pki.ca.certprofile.commonpki.ProfessionInfoOption;
import org.xipki.pki.ca.certprofile.commonpki.RegistrationNumberOption;
import org.xipki.pki.ca.certprofile.x509.jaxb.AdmissionSyntax;
import org.xipki.pki.ca.certprofile.x509.jaxb.AdmissionsType;
import org.xipki.pki.ca.certprofile.x509.jaxb.AlgorithmType;
import org.xipki.pki.ca.certprofile.x509.jaxb.CertificatePolicies;
import org.xipki.pki.ca.certprofile.x509.jaxb.CertificatePolicyInformationType;
import org.xipki.pki.ca.certprofile.x509.jaxb.CertificatePolicyInformationType.PolicyQualifiers;
import org.xipki.pki.ca.certprofile.x509.jaxb.ConstantExtValue;
import org.xipki.pki.ca.certprofile.x509.jaxb.DHParameters;
import org.xipki.pki.ca.certprofile.x509.jaxb.DSAParameters;
import org.xipki.pki.ca.certprofile.x509.jaxb.ECParameters;
import org.xipki.pki.ca.certprofile.x509.jaxb.ECParameters.Curves;
import org.xipki.pki.ca.certprofile.x509.jaxb.ExtendedKeyUsage;
import org.xipki.pki.ca.certprofile.x509.jaxb.ExtendedKeyUsage.Usage;
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.GeneralNameType;
import org.xipki.pki.ca.certprofile.x509.jaxb.GeneralSubtreeBaseType;
import org.xipki.pki.ca.certprofile.x509.jaxb.GeneralSubtreesType;
import org.xipki.pki.ca.certprofile.x509.jaxb.GostParameters;
import org.xipki.pki.ca.certprofile.x509.jaxb.NamingAuthorityType;
import org.xipki.pki.ca.certprofile.x509.jaxb.ObjectFactory;
import org.xipki.pki.ca.certprofile.x509.jaxb.OidWithDescType;
import org.xipki.pki.ca.certprofile.x509.jaxb.PolicyConstraints;
import org.xipki.pki.ca.certprofile.x509.jaxb.PolicyIdMappingType;
import org.xipki.pki.ca.certprofile.x509.jaxb.ProfessionInfoType;
import org.xipki.pki.ca.certprofile.x509.jaxb.ProfessionInfoType.RegistrationNumber;
import org.xipki.pki.ca.certprofile.x509.jaxb.RSAPSSParameters;
import org.xipki.pki.ca.certprofile.x509.jaxb.RSAParameters;
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.UsageType;
import org.xipki.pki.ca.certprofile.x509.jaxb.X509ProfileType;
import org.xipki.pki.ca.certprofile.x509.jaxb.X509ProfileType.KeyAlgorithms;
import org.xml.sax.SAXException;

/**
 * @author Lijun Liao
 * @since 2.0.0
 */

public class XmlX509CertprofileUtil {

    private static final Logger LOG = LoggerFactory.getLogger(XmlX509CertprofileUtil.class);

    private static final Object JAXB_LOCK = new Object();

    private static Unmarshaller jaxbUnmarshaller;

    private XmlX509CertprofileUtil() {
    }

    public static X509ProfileType parse(final InputStream xmlConfStream) throws CertprofileException {
        ParamUtil.requireNonNull("xmlConfStream", xmlConfStream);
        synchronized (JAXB_LOCK) {
            JAXBElement<?> rootElement;
            try {
                if (jaxbUnmarshaller == null) {
                    JAXBContext context = JAXBContext.newInstance(ObjectFactory.class);
                    jaxbUnmarshaller = context.createUnmarshaller();

                    final SchemaFactory schemaFact = SchemaFactory
                            .newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
                    URL url = XmlX509CertprofileUtil.class.getResource("/xsd/certprofile.xsd");
                    jaxbUnmarshaller.setSchema(schemaFact.newSchema(url));
                }

                rootElement = (JAXBElement<?>) jaxbUnmarshaller.unmarshal(xmlConfStream);
            } catch (SAXException ex) {
                throw new CertprofileException("parse profile failed, message: " + ex.getMessage(), ex);
            } catch (JAXBException ex) {
                throw new CertprofileException("parse profile failed, message: " + XmlUtil.getMessage(ex), ex);
            }

            try {
                xmlConfStream.close();
            } catch (IOException ex) {
                LOG.warn("could not close xmlConfStream: {}", ex.getMessage());
            }

            Object rootType = rootElement.getValue();
            if (rootType instanceof X509ProfileType) {
                return (X509ProfileType) rootElement.getValue();
            } else {
                throw new CertprofileException("invalid root element type");
            }
        }
    } // method parse

    public static List<CertificatePolicyInformation> buildCertificatePolicies(final CertificatePolicies type) {
        List<CertificatePolicyInformationType> policyPairs = type.getCertificatePolicyInformation();

        List<CertificatePolicyInformation> policies = new ArrayList<CertificatePolicyInformation>(
                policyPairs.size());
        for (CertificatePolicyInformationType policyPair : policyPairs) {
            List<CertificatePolicyQualifier> qualifiers = null;

            PolicyQualifiers policyQualifiers = policyPair.getPolicyQualifiers();
            if (policyQualifiers != null) {
                List<JAXBElement<String>> cpsUriOrUserNotice = policyQualifiers.getCpsUriOrUserNotice();

                qualifiers = new ArrayList<CertificatePolicyQualifier>(cpsUriOrUserNotice.size());
                for (JAXBElement<String> element : cpsUriOrUserNotice) {
                    String elementValue = element.getValue();
                    CertificatePolicyQualifier qualifier = null;
                    String elementName = element.getName().getLocalPart();
                    qualifier = "cpsUri".equals(elementName)
                            ? CertificatePolicyQualifier.getInstanceForCpsUri(elementValue)
                            : CertificatePolicyQualifier.getInstanceForUserNotice(elementValue);
                    qualifiers.add(qualifier);
                }
            }

            CertificatePolicyInformation cpi = new CertificatePolicyInformation(
                    policyPair.getPolicyIdentifier().getValue(), qualifiers);
            policies.add(cpi);
        }

        return policies;
    } // method buildCertificatePolicies

    public static PolicyMappings buildPolicyMappings(
            final org.xipki.pki.ca.certprofile.x509.jaxb.PolicyMappings type) {
        ParamUtil.requireNonNull("type", type);
        List<PolicyIdMappingType> mappings = type.getMapping();
        final int n = mappings.size();

        CertPolicyId[] issuerDomainPolicy = new CertPolicyId[n];
        CertPolicyId[] subjectDomainPolicy = new CertPolicyId[n];

        for (int i = 0; i < n; i++) {
            PolicyIdMappingType mapping = mappings.get(i);
            ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(mapping.getIssuerDomainPolicy().getValue());
            issuerDomainPolicy[i] = CertPolicyId.getInstance(oid);

            oid = new ASN1ObjectIdentifier(mapping.getSubjectDomainPolicy().getValue());
            subjectDomainPolicy[i] = CertPolicyId.getInstance(oid);
        }

        return new PolicyMappings(issuerDomainPolicy, subjectDomainPolicy);
    } // method buildPolicyMappings

    public static NameConstraints buildNameConstrains(
            final org.xipki.pki.ca.certprofile.x509.jaxb.NameConstraints type) throws CertprofileException {
        ParamUtil.requireNonNull("type", type);
        GeneralSubtree[] permitted = buildGeneralSubtrees(type.getPermittedSubtrees());
        GeneralSubtree[] excluded = buildGeneralSubtrees(type.getExcludedSubtrees());
        return (permitted == null && excluded == null) ? null : new NameConstraints(permitted, excluded);
    } // method buildNameConstrains

    private static GeneralSubtree[] buildGeneralSubtrees(final GeneralSubtreesType subtrees)
            throws CertprofileException {
        if (subtrees == null || CollectionUtil.isEmpty(subtrees.getBase())) {
            return null;
        }

        List<GeneralSubtreeBaseType> list = subtrees.getBase();
        final int n = list.size();
        GeneralSubtree[] ret = new GeneralSubtree[n];
        for (int i = 0; i < n; i++) {
            ret[i] = buildGeneralSubtree(list.get(i));
        }

        return ret;
    } // method buildGeneralSubtrees

    private static GeneralSubtree buildGeneralSubtree(final GeneralSubtreeBaseType type)
            throws CertprofileException {
        ParamUtil.requireNonNull("type", type);
        GeneralName base = null;
        if (type.getDirectoryName() != null) {
            base = new GeneralName(X509Util.reverse(new X500Name(type.getDirectoryName())));
        } else if (type.getDnsName() != null) {
            base = new GeneralName(GeneralName.dNSName, type.getDnsName());
        } else if (type.getIpAddress() != null) {
            base = new GeneralName(GeneralName.iPAddress, type.getIpAddress());
        } else if (type.getRfc822Name() != null) {
            base = new GeneralName(GeneralName.rfc822Name, type.getRfc822Name());
        } else if (type.getUri() != null) {
            base = new GeneralName(GeneralName.uniformResourceIdentifier, type.getUri());
        } else {
            throw new RuntimeException("should not reach here, unknown child of GeneralSubtreeBaseType");
        }

        Integer min = type.getMinimum();
        if (min != null && min < 0) {
            throw new CertprofileException("negative minimum is not allowed: " + min);
        }
        BigInteger minimum = (min == null) ? null : BigInteger.valueOf(min.intValue());

        Integer max = type.getMaximum();
        if (max != null && max < 0) {
            throw new CertprofileException("negative maximum is not allowed: " + max);
        }
        BigInteger maximum = (max == null) ? null : BigInteger.valueOf(max.intValue());

        return new GeneralSubtree(base, minimum, maximum);
    } // method buildGeneralSubtree

    public static ASN1Sequence buildPolicyConstrains(final PolicyConstraints type) throws CertprofileException {
        ParamUtil.requireNonNull("type", type);
        Integer requireExplicitPolicy = type.getRequireExplicitPolicy();
        if (requireExplicitPolicy != null && requireExplicitPolicy < 0) {
            throw new CertprofileException(
                    "negative requireExplicitPolicy is not allowed: " + requireExplicitPolicy);
        }

        Integer inhibitPolicyMapping = type.getInhibitPolicyMapping();
        if (inhibitPolicyMapping != null && inhibitPolicyMapping < 0) {
            throw new CertprofileException("negative inhibitPolicyMapping is not allowed: " + inhibitPolicyMapping);
        }

        if (requireExplicitPolicy == null && inhibitPolicyMapping == null) {
            return null;
        }

        final boolean explicit = false;
        ASN1EncodableVector vec = new ASN1EncodableVector();
        if (requireExplicitPolicy != null) {
            vec.add(new DERTaggedObject(explicit, 0, new ASN1Integer(BigInteger.valueOf(requireExplicitPolicy))));
        }

        if (inhibitPolicyMapping != null) {
            vec.add(new DERTaggedObject(explicit, 1, new ASN1Integer(BigInteger.valueOf(inhibitPolicyMapping))));
        }

        return new DERSequence(vec);
    } //method buildPolicyConstrains

    public static Set<GeneralNameMode> buildGeneralNameMode(final GeneralNameType name)
            throws CertprofileException {
        ParamUtil.requireNonNull("name", name);

        Set<GeneralNameMode> ret = new HashSet<>();
        if (name.getOtherName() != null) {
            List<OidWithDescType> list = name.getOtherName().getType();
            Set<ASN1ObjectIdentifier> set = new HashSet<>();
            for (OidWithDescType entry : list) {
                set.add(new ASN1ObjectIdentifier(entry.getValue()));
            }
            ret.add(new GeneralNameMode(GeneralNameTag.otherName, set));
        }

        if (name.getRfc822Name() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.rfc822Name));
        }

        if (name.getDnsName() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.dNSName));
        }

        if (name.getDirectoryName() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.directoryName));
        }

        if (name.getEdiPartyName() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.ediPartyName));
        }

        if (name.getUniformResourceIdentifier() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.uniformResourceIdentifier));
        }

        if (name.getIpAddress() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.iPAddress));
        }

        if (name.getRegisteredID() != null) {
            ret.add(new GeneralNameMode(GeneralNameTag.registeredID));
        }

        if (ret.isEmpty()) {
            throw new CertprofileException("GeneralNameType must not be empty");
        }

        return ret;
    } // method buildGeneralNameMode

    private 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;
    }

    public static Map<ASN1ObjectIdentifier, KeyParametersOption> buildKeyAlgorithms(final KeyAlgorithms keyAlgos)
            throws CertprofileException {
        ParamUtil.requireNonNull("keyAlgos", keyAlgos);
        Map<ASN1ObjectIdentifier, KeyParametersOption> keyAlgorithms = new HashMap<>();
        for (AlgorithmType type : keyAlgos.getAlgorithm()) {
            List<OidWithDescType> algIds = type.getAlgorithm();
            List<ASN1ObjectIdentifier> oids = new ArrayList<>(algIds.size());
            for (OidWithDescType algId : algIds) {
                ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(algId.getValue());
                if (keyAlgorithms.containsKey(oid)) {
                    throw new CertprofileException("duplicate definition of keyAlgorithm " + oid.getId());
                }
                oids.add(oid);
            }

            KeyParametersOption keyParamsOption = convertKeyParametersOption(type);
            for (ASN1ObjectIdentifier oid : oids) {
                keyAlgorithms.put(oid, keyParamsOption);
            }
        }
        return CollectionUtil.unmodifiableMap(keyAlgorithms);
    } // method buildKeyAlgorithms

    public static Map<ASN1ObjectIdentifier, ExtensionControl> buildExtensionControls(
            final ExtensionsType extensionsType) throws CertprofileException {
        ParamUtil.requireNonNull("extensionsType", extensionsType);
        // Extension controls
        Map<ASN1ObjectIdentifier, ExtensionControl> controls = new HashMap<>();
        for (ExtensionType m : extensionsType.getExtension()) {
            ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(m.getType().getValue());
            if (controls.containsKey(oid)) {
                throw new CertprofileException("duplicated definition of extension " + oid.getId());
            }
            ExtensionControl ctrl = new ExtensionControl(m.isCritical(), m.isRequired(), m.isPermittedInRequest());
            controls.put(oid, ctrl);
        }

        return Collections.unmodifiableMap(controls);
    } // method buildExtensionControls

    public static List<ASN1ObjectIdentifier> toOidList(final List<OidWithDescType> oidWithDescTypes) {
        if (CollectionUtil.isEmpty(oidWithDescTypes)) {
            return null;
        }

        List<ASN1ObjectIdentifier> oids = new LinkedList<>();
        for (OidWithDescType type : oidWithDescTypes) {
            oids.add(new ASN1ObjectIdentifier(type.getValue()));
        }
        return Collections.unmodifiableList(oids);
    } // method toOidList

    public static Set<KeyUsageControl> buildKeyUsageOptions(
            final org.xipki.pki.ca.certprofile.x509.jaxb.KeyUsage extConf) {
        ParamUtil.requireNonNull("extConf", extConf);
        List<UsageType> usages = extConf.getUsage();
        Set<KeyUsageControl> controls = new HashSet<>();

        for (UsageType m : usages) {
            boolean required = m.isRequired();
            switch (m.getValue()) {
            case CRL_SIGN:
                controls.add(new KeyUsageControl(KeyUsage.cRLSign, required));
                break;
            case DATA_ENCIPHERMENT:
                controls.add(new KeyUsageControl(KeyUsage.dataEncipherment, required));
                break;
            case CONTENT_COMMITMENT:
                controls.add(new KeyUsageControl(KeyUsage.contentCommitment, required));
                break;
            case DECIPHER_ONLY:
                controls.add(new KeyUsageControl(KeyUsage.decipherOnly, required));
                break;
            case ENCIPHER_ONLY:
                controls.add(new KeyUsageControl(KeyUsage.encipherOnly, required));
                break;
            case DIGITAL_SIGNATURE:
                controls.add(new KeyUsageControl(KeyUsage.digitalSignature, required));
                break;
            case KEY_AGREEMENT:
                controls.add(new KeyUsageControl(KeyUsage.keyAgreement, required));
                break;
            case KEY_CERT_SIGN:
                controls.add(new KeyUsageControl(KeyUsage.keyCertSign, required));
                break;
            case KEY_ENCIPHERMENT:
                controls.add(new KeyUsageControl(KeyUsage.keyEncipherment, required));
                break;
            default:
                throw new RuntimeException("should not reach here, unknown GeneralSubtreeBaseType " + m.getValue());
            }
        }

        return Collections.unmodifiableSet(controls);
    } // method buildKeyUsageOptions

    public static Set<ExtKeyUsageControl> buildExtKeyUsageOptions(final ExtendedKeyUsage extConf) {
        ParamUtil.requireNonNull("extConf", extConf);
        List<Usage> usages = extConf.getUsage();
        Set<ExtKeyUsageControl> controls = new HashSet<>();

        for (Usage m : usages) {
            ExtKeyUsageControl usage = new ExtKeyUsageControl(new ASN1ObjectIdentifier(m.getValue()),
                    m.isRequired());
            controls.add(usage);
        }

        return Collections.unmodifiableSet(controls);
    } // method buildExtKeyUsageOptions

    public static Map<ASN1ObjectIdentifier, ExtensionValue> buildConstantExtesions(
            final ExtensionsType extensionsType) throws CertprofileException {
        if (extensionsType == null) {
            return null;
        }

        Map<ASN1ObjectIdentifier, ExtensionValue> map = new HashMap<>();

        for (ExtensionType m : extensionsType.getExtension()) {
            ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(m.getType().getValue());
            if (Extension.subjectAlternativeName.equals(oid) || Extension.subjectInfoAccess.equals(oid)
                    || Extension.biometricInfo.equals(oid)) {
                continue;
            }

            if (m.getValue() == null || !(m.getValue().getAny() instanceof ConstantExtValue)) {
                continue;
            }

            ConstantExtValue extConf = (ConstantExtValue) m.getValue().getAny();
            byte[] encodedValue = extConf.getValue();
            ASN1StreamParser parser = new ASN1StreamParser(encodedValue);
            ASN1Encodable value;
            try {
                value = parser.readObject();
            } catch (IOException ex) {
                throw new CertprofileException("could not parse the constant extension value", ex);
            }
            ExtensionValue extension = new ExtensionValue(m.isCritical(), value);
            map.put(oid, extension);
        }

        if (CollectionUtil.isEmpty(map)) {
            return null;
        }

        return Collections.unmodifiableMap(map);
    } // buildConstantExtesions

    public static Set<ASN1ObjectIdentifier> toOidSet(final List<OidWithDescType> oidWithDescTypes) {
        if (CollectionUtil.isEmpty(oidWithDescTypes)) {
            return null;
        }

        Set<ASN1ObjectIdentifier> oids = new HashSet<>();
        for (OidWithDescType type : oidWithDescTypes) {
            oids.add(new ASN1ObjectIdentifier(type.getValue()));
        }
        return Collections.unmodifiableSet(oids);
    }

    public static AdmissionSyntaxOption buildAdmissionSyntax(final boolean critical, final AdmissionSyntax type)
            throws CertprofileException {
        List<AdmissionsOption> admissionsList = new LinkedList<>();
        for (AdmissionsType at : type.getContentsOfAdmissions()) {
            List<ProfessionInfoOption> professionInfos = new LinkedList<>();
            for (ProfessionInfoType pi : at.getProfessionInfo()) {
                NamingAuthority namingAuthorityL3 = null;
                if (pi.getNamingAuthority() != null) {
                    namingAuthorityL3 = buildNamingAuthority(pi.getNamingAuthority());
                }

                List<OidWithDescType> oidTypes = pi.getProfessionOid();
                List<ASN1ObjectIdentifier> oids = null;
                if (CollectionUtil.isNonEmpty(oidTypes)) {
                    oids = new LinkedList<>();
                    for (OidWithDescType k : oidTypes) {
                        oids.add(new ASN1ObjectIdentifier(k.getValue()));
                    }
                }

                RegistrationNumber rnType = pi.getRegistrationNumber();
                RegistrationNumberOption rno = (rnType == null) ? null
                        : new RegistrationNumberOption(rnType.getRegex(), rnType.getConstant());

                ProfessionInfoOption pio = new ProfessionInfoOption(namingAuthorityL3, pi.getProfessionItem(), oids,
                        rno, pi.getAddProfessionInfo());

                professionInfos.add(pio);
            }

            GeneralName admissionAuthority = null;
            if (at.getNamingAuthority() != null) {
                admissionAuthority = GeneralName
                        .getInstance(asn1PrimitivefromByteArray(at.getAdmissionAuthority()));
            }

            NamingAuthority namingAuthority = null;
            if (at.getNamingAuthority() != null) {
                namingAuthority = buildNamingAuthority(at.getNamingAuthority());
            }

            AdmissionsOption admissionsOption = new AdmissionsOption(admissionAuthority, namingAuthority,
                    professionInfos);
            admissionsList.add(admissionsOption);
        }

        GeneralName admissionAuthority = null;
        if (type.getAdmissionAuthority() != null) {
            admissionAuthority = GeneralName.getInstance(type.getAdmissionAuthority());
        }

        return new AdmissionSyntaxOption(critical, admissionAuthority, admissionsList);
    }

    private static ASN1Primitive asn1PrimitivefromByteArray(final byte[] encoded) throws CertprofileException {
        try {
            return ASN1Primitive.fromByteArray(encoded);
        } catch (IOException ex) {
            throw new CertprofileException(ex.getMessage(), ex);
        }
    }

    private static KeyParametersOption convertKeyParametersOption(final AlgorithmType type)
            throws CertprofileException {
        ParamUtil.requireNonNull("type", type);
        if (type.getParameters() == null || type.getParameters().getAny() == null) {
            return KeyParametersOption.ALLOW_ALL;
        }

        Object paramsObj = type.getParameters().getAny();
        if (paramsObj instanceof ECParameters) {
            ECParameters params = (ECParameters) paramsObj;
            KeyParametersOption.ECParamatersOption option = new KeyParametersOption.ECParamatersOption();

            if (params.getCurves() != null) {
                Curves curves = params.getCurves();
                Set<ASN1ObjectIdentifier> curveOids = toOidSet(curves.getCurve());
                option.setCurveOids(curveOids);
            }

            if (params.getPointEncodings() != null) {
                List<Byte> bytes = params.getPointEncodings().getPointEncoding();
                Set<Byte> pointEncodings = new HashSet<>(bytes);
                option.setPointEncodings(pointEncodings);
            }

            return option;
        } else if (paramsObj instanceof RSAParameters) {
            RSAParameters params = (RSAParameters) paramsObj;
            KeyParametersOption.RSAParametersOption option = new KeyParametersOption.RSAParametersOption();

            Set<Range> modulusLengths = buildParametersMap(params.getModulusLength());
            option.setModulusLengths(modulusLengths);

            return option;
        } else if (paramsObj instanceof RSAPSSParameters) {
            RSAPSSParameters params = (RSAPSSParameters) paramsObj;
            KeyParametersOption.RSAPSSParametersOption option = new KeyParametersOption.RSAPSSParametersOption();

            Set<Range> modulusLengths = buildParametersMap(params.getModulusLength());
            option.setModulusLengths(modulusLengths);

            return option;
        } else if (paramsObj instanceof DSAParameters) {
            DSAParameters params = (DSAParameters) paramsObj;
            KeyParametersOption.DSAParametersOption option = new KeyParametersOption.DSAParametersOption();

            Set<Range> plengths = buildParametersMap(params.getPLength());
            option.setPlengths(plengths);

            Set<Range> qlengths = buildParametersMap(params.getQLength());
            option.setQlengths(qlengths);

            return option;
        } else if (paramsObj instanceof DHParameters) {
            DHParameters params = (DHParameters) paramsObj;
            KeyParametersOption.DHParametersOption option = new KeyParametersOption.DHParametersOption();

            Set<Range> plengths = buildParametersMap(params.getPLength());
            option.setPlengths(plengths);

            Set<Range> qlengths = buildParametersMap(params.getQLength());
            option.setQlengths(qlengths);

            return option;
        } else if (paramsObj instanceof GostParameters) {
            GostParameters params = (GostParameters) paramsObj;
            KeyParametersOption.GostParametersOption option = new KeyParametersOption.GostParametersOption();

            Set<ASN1ObjectIdentifier> set = toOidSet(params.getPublicKeyParamSet());
            option.setPublicKeyParamSets(set);

            set = toOidSet(params.getDigestParamSet());
            option.setDigestParamSets(set);

            set = toOidSet(params.getEncryptionParamSet());
            option.setEncryptionParamSets(set);

            return option;
        } else {
            throw new CertprofileException("unknown public key parameters type " + paramsObj.getClass().getName());
        }
    } // method convertKeyParametersOption

    public static final DirectoryStringType convertDirectoryStringType(
            final org.xipki.pki.ca.certprofile.x509.jaxb.DirectoryStringType jaxbType) {
        if (jaxbType == null) {
            return null;
        }

        switch (jaxbType) {
        case BMP_STRING:
            return DirectoryStringType.bmpString;
        case PRINTABLE_STRING:
            return DirectoryStringType.printableString;
        case TELETEX_STRING:
            return DirectoryStringType.teletexString;
        case UTF_8_STRING:
            return DirectoryStringType.utf8String;
        default:
            throw new RuntimeException("should not reach here, undefined DirectoryStringType " + jaxbType);
        }
    }

    public static final StringType convertStringType(
            final org.xipki.pki.ca.certprofile.x509.jaxb.StringType jaxbType) {
        if (jaxbType == null) {
            return null;
        }

        switch (jaxbType) {
        case BMP_STRING:
            return StringType.bmpString;
        case PRINTABLE_STRING:
            return StringType.printableString;
        case TELETEX_STRING:
            return StringType.teletexString;
        case UTF_8_STRING:
            return StringType.utf8String;
        case IA_5_STRING:
            return StringType.ia5String;
        default:
            throw new RuntimeException("should not reach here, undefined StringType " + jaxbType);
        }
    }

    public static org.bouncycastle.asn1.x509.CertificatePolicies createCertificatePolicies(
            final List<CertificatePolicyInformation> policyInfos) throws CertprofileException {
        ParamUtil.requireNonEmpty("policyInfos", policyInfos);

        int size = policyInfos.size();
        PolicyInformation[] infos = new PolicyInformation[size];

        int idx = 0;
        for (CertificatePolicyInformation policyInfo : policyInfos) {
            String policyId = policyInfo.getCertPolicyId();
            List<CertificatePolicyQualifier> qualifiers = policyInfo.getQualifiers();

            ASN1Sequence policyQualifiers = null;
            if (CollectionUtil.isNonEmpty(qualifiers)) {
                policyQualifiers = createPolicyQualifiers(qualifiers);
            }

            ASN1ObjectIdentifier policyOid = new ASN1ObjectIdentifier(policyId);
            infos[idx++] = (policyQualifiers == null) ? new PolicyInformation(policyOid)
                    : new PolicyInformation(policyOid, policyQualifiers);
        }

        return new org.bouncycastle.asn1.x509.CertificatePolicies(infos);
    }

    private static ASN1Sequence createPolicyQualifiers(final List<CertificatePolicyQualifier> qualifiers) {
        ParamUtil.requireNonNull("qualifiers", qualifiers);
        List<PolicyQualifierInfo> qualifierInfos = new ArrayList<>(qualifiers.size());
        for (CertificatePolicyQualifier qualifier : qualifiers) {
            PolicyQualifierInfo qualifierInfo;
            if (qualifier.getCpsUri() != null) {
                qualifierInfo = new PolicyQualifierInfo(qualifier.getCpsUri());
            } else if (qualifier.getUserNotice() != null) {
                UserNotice userNotice = new UserNotice(null, qualifier.getUserNotice());
                qualifierInfo = new PolicyQualifierInfo(PKCSObjectIdentifiers.id_spq_ets_unotice, userNotice);
            } else {
                qualifierInfo = null;
            }

            if (qualifierInfo != null) {
                qualifierInfos.add(qualifierInfo);
            }
            //PolicyQualifierId qualifierId
        }

        return new DERSequence(qualifierInfos.toArray(new PolicyQualifierInfo[0]));
    }

    private static NamingAuthority buildNamingAuthority(final NamingAuthorityType jaxb) {
        ASN1ObjectIdentifier oid = (jaxb.getOid() == null) ? null
                : new ASN1ObjectIdentifier(jaxb.getOid().getValue());
        String url = StringUtil.isBlank(jaxb.getUrl()) ? null : jaxb.getUrl();
        DirectoryString text = StringUtil.isBlank(jaxb.getText()) ? null : new DirectoryString(jaxb.getText());
        return new NamingAuthority(oid, url, text);
    }

}