mitm.common.security.certificate.impl.StandardX509CertificateBuilder.java Source code

Java tutorial

Introduction

Here is the source code for mitm.common.security.certificate.impl.StandardX509CertificateBuilder.java

Source

/*
 * Copyright (c) 2008-2012, Martijn Brinkers, Djigzo.
 * 
 * This file is part of Djigzo email encryption.
 *
 * Djigzo is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License 
 * version 3, 19 November 2007 as published by the Free Software 
 * Foundation.
 *
 * Djigzo 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 Djigzo. If not, see <http://www.gnu.org/licenses/>
 *
 * Additional permission under GNU AGPL version 3 section 7
 * 
 * If you modify this Program, or any covered work, by linking or 
 * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, 
 * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, 
 * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, 
 * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, 
 * wsdl4j-1.6.1.jar (or modified versions of these libraries), 
 * containing parts covered by the terms of Eclipse Public License, 
 * tyrex license, freemarker license, dom4j license, mx4j license,
 * Spice Software License, Common Development and Distribution License
 * (CDDL), Common Public License (CPL) the licensors of this Program grant 
 * you additional permission to convey the resulting work.
 */
package mitm.common.security.certificate.impl;

import java.io.IOException;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.CertificateException;
import java.security.cert.CertificateParsingException;
import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.Date;
import java.util.Set;
import java.util.Vector;

import javax.security.auth.x500.X500Principal;

import mitm.common.security.certificate.CertificateBuilderException;
import mitm.common.security.certificate.CertificateVersion;
import mitm.common.security.certificate.ExtendedKeyUsageType;
import mitm.common.security.certificate.KeyUsageType;
import mitm.common.security.certificate.X500PrincipalUtils;
import mitm.common.security.certificate.X509CertificateBuilder;
import mitm.common.security.certificate.X509CertificateInspector;
import mitm.common.security.crl.CRLDistributionPointsBuilder;

import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.KeyPurposeId;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x509.X509Extension;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v1CertificateBuilder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

/**
 * Implementation of {@link X509CertificateBuilder}.
 * 
 * @author Martijn Brinkers
 *
 */
public class StandardX509CertificateBuilder implements X509CertificateBuilder {
    /* 
     * the security provider for signing the certificate
     */
    private final String signingProvider;

    /* 
     * the security provider for creating the certificate
     */
    private final String certificateProvider;

    private CertificateVersion version = CertificateVersion.V3;
    private BigInteger serialNumber;
    private X500Principal subject;
    private X500Principal issuer;
    private Date notBefore;
    private Date notAfter;
    private String signatureAlgorithm;
    private GeneralNames altNames;
    private boolean altNamesCritical;
    private Set<KeyUsageType> keyUsage;
    private boolean keyUsageCritical;
    private Set<ExtendedKeyUsageType> extendedKeyUsage;
    private boolean extendedKeyUsageCritical;
    private PublicKey publicKey;
    private Integer pathLengthConstraint;
    private boolean isCA;
    private boolean cACritical;
    private boolean addSubjectKeyIdentier;
    private boolean addAuthorityKeyIdentier = true;
    private Collection<String> crlDistributionPointURIs;

    public StandardX509CertificateBuilder(String signingProvider, String certificateProvider) {
        this.signingProvider = signingProvider;
        this.certificateProvider = certificateProvider;
    }

    @Override
    public void setVersion(CertificateVersion version) {
        this.version = version;
    }

    @Override
    public CertificateVersion getVersion() {
        return version;
    }

    @Override
    public void setSerialNumber(BigInteger serialNumber) {
        this.serialNumber = serialNumber;
    }

    @Override
    public BigInteger getSerialNumber() {
        return serialNumber;
    }

    @Override
    public void setSubject(X500Principal subject) {
        this.subject = subject;
    }

    @Override
    public X500Principal getSubject() {
        return subject;
    }

    @Override
    public void setIssuer(X500Principal issuer) {
        this.issuer = issuer;
    }

    @Override
    public X500Principal getIssuer() {
        return issuer;
    }

    @Override
    public void setNotBefore(Date date) {
        this.notBefore = date;
    }

    @Override
    public Date getNotBefore() {
        return notBefore;
    }

    @Override
    public void setNotAfter(Date date) {
        this.notAfter = date;
    }

    @Override
    public Date getNotAfter() {
        return notAfter;
    }

    @Override
    public void setSignatureAlgorithm(String signatureAlgorithm) {
        this.signatureAlgorithm = signatureAlgorithm;
    }

    @Override
    public String getSignatureAlgorithm() {
        return signatureAlgorithm;
    }

    @Override
    public void setAltNames(GeneralNames altNames, boolean critical) {
        this.altNames = altNames;
        this.altNamesCritical = critical;
    }

    @Override
    public GeneralNames getAltNames() {
        return altNames;
    }

    @Override
    public boolean isAltNamesCritical() {
        return altNamesCritical;
    }

    @Override
    public void setKeyUsage(Set<KeyUsageType> keyUsage, boolean critical) {
        this.keyUsage = keyUsage;
        this.keyUsageCritical = critical;
    }

    @Override
    public Set<KeyUsageType> getKeyUsage() {
        return keyUsage;
    }

    @Override
    public boolean isKeyUsageCritical() {
        return keyUsageCritical;
    }

    @Override
    public void setExtendedKeyUsage(Set<ExtendedKeyUsageType> keyUsage, boolean critical) {
        this.extendedKeyUsage = keyUsage;
        this.extendedKeyUsageCritical = critical;
    }

    @Override
    public Set<ExtendedKeyUsageType> getExtendedKeyUsage() {
        return extendedKeyUsage;
    }

    @Override
    public boolean isExtendedKeyUsageCritical() {
        return extendedKeyUsageCritical;
    }

    @Override
    public void setIsCA(boolean isCA, boolean critical) {
        this.isCA = isCA;
        this.cACritical = critical;
    }

    @Override
    public boolean isCA() {
        return isCA;
    }

    @Override
    public boolean isCACritical() {
        return cACritical;
    }

    @Override
    public void addSubjectKeyIdentifier(boolean add) {
        this.addSubjectKeyIdentier = add;
    }

    @Override
    public boolean isAddSubjectKeyIdentifier() {
        return addSubjectKeyIdentier;
    }

    @Override
    public void addAuthorityKeyIdentifier(boolean add) {
        this.addAuthorityKeyIdentier = add;
    }

    @Override
    public boolean isAddAuthorityKeyIdentifier() {
        return addAuthorityKeyIdentier;
    }

    @Override
    public void setPathLengthConstraint(Integer pathLengthConstraint) {
        this.pathLengthConstraint = pathLengthConstraint;
    }

    @Override
    public Integer getPathLengthConstraint() {
        return pathLengthConstraint;
    }

    @Override
    public void setPublicKey(PublicKey publicKey) {
        this.publicKey = publicKey;
    }

    @Override
    public PublicKey getPublicKey() {
        return publicKey;
    }

    @Override
    public void setCRLDistributionPoints(Collection<String> uris) {
        this.crlDistributionPointURIs = uris;
    }

    /*
     * Helper function to build a KeyUsage instance for certificate creation
     */
    protected KeyUsage getKeyUsageASN1() {
        int keyUsageBits = 0;

        for (KeyUsageType type : keyUsage) {
            keyUsageBits = keyUsageBits | type.getBitValue();
        }

        return new KeyUsage(keyUsageBits);
    }

    /*
     * Helper function to build a ExtendedKeyUsage instance for certificate creation
     */
    protected ExtendedKeyUsage getExtendedKeyUsageASN1() {
        Vector<KeyPurposeId> keyPurposes = new Vector<KeyPurposeId>();

        for (ExtendedKeyUsageType type : extendedKeyUsage) {
            keyPurposes.add(type.getKeyPurposeId());
        }

        ExtendedKeyUsage usage = new ExtendedKeyUsage(keyPurposes);

        return usage;
    }

    /*
     * Add the key identifier using a SHA1 hash over the BIT STRING
     * from SubjectPublicKeyInfo as defined in RFC2459.
     */
    protected SubjectKeyIdentifier createSubjectKeyIdentifier(PublicKey publicKey) throws NoSuchAlgorithmException {
        return new JcaX509ExtensionUtils().createSubjectKeyIdentifier(publicKey);
    }

    protected AuthorityKeyIdentifier getAuthorityKeyIdentifier(X509Certificate issuerCertificate)
            throws CertificateParsingException, IOException {
        X509CertificateInspector inspector = new X509CertificateInspector(issuerCertificate);

        /*
         * We must add the issuer of the issuer certificate! not the subject of the issuer certificate
         * The subject / serial number is already contained in the certificate to be issued. The 
         * issuer of the issuer is added to identify multiple paths if an intermediate can be signed
         * by multiple ca's
         */
        GeneralNames names = new GeneralNames(new GeneralName(inspector.getIssuerX500Name()));

        AuthorityKeyIdentifier authorityKeyIdentifier;

        byte[] subjectKeyIdentifier = inspector.getSubjectKeyIdentifier();

        if (subjectKeyIdentifier != null) {
            authorityKeyIdentifier = new AuthorityKeyIdentifier(subjectKeyIdentifier, names,
                    issuerCertificate.getSerialNumber());
        } else {
            authorityKeyIdentifier = new AuthorityKeyIdentifier(names, issuerCertificate.getSerialNumber());
        }

        return authorityKeyIdentifier;
    }

    protected X509v3CertificateBuilder createX509v3CertificateBuilder(X509Certificate issuerCertificate)
            throws IOException, CertificateParsingException, NoSuchAlgorithmException {
        X500Principal issuerPrincipal = getIssuer();

        if (issuerCertificate != null) {
            issuerPrincipal = issuerCertificate.getSubjectX500Principal();
        }

        X509v3CertificateBuilder builder = new X509v3CertificateBuilder(
                X500PrincipalUtils.toX500Name(issuerPrincipal), serialNumber, notBefore, notAfter,
                X500PrincipalUtils.toX500Name(subject), SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));

        if (isCA) {
            BasicConstraints basicConstraints = pathLengthConstraint == null ? new BasicConstraints(true)
                    : new BasicConstraints(pathLengthConstraint);

            builder.addExtension(X509Extension.basicConstraints, cACritical, basicConstraints);
        }

        if (keyUsage != null && keyUsage.size() > 0) {
            builder.addExtension(X509Extension.keyUsage, keyUsageCritical, getKeyUsageASN1());
        }

        if (extendedKeyUsage != null && extendedKeyUsage.size() > 0) {
            builder.addExtension(X509Extension.extendedKeyUsage, extendedKeyUsageCritical,
                    getExtendedKeyUsageASN1());
        }

        if (altNames != null) {
            builder.addExtension(X509Extension.subjectAlternativeName, altNamesCritical, altNames);
        }

        if (addSubjectKeyIdentier) {
            builder.addExtension(X509Extension.subjectKeyIdentifier, false, createSubjectKeyIdentifier(publicKey));
        }

        if (issuerCertificate != null && addAuthorityKeyIdentier) {
            builder.addExtension(X509Extension.authorityKeyIdentifier, false,
                    getAuthorityKeyIdentifier(issuerCertificate));
        }

        if (crlDistributionPointURIs != null && crlDistributionPointURIs.size() > 0) {
            CRLDistributionPointsBuilder distPointBuilder = new CRLDistributionPointsBuilder();

            for (String uri : crlDistributionPointURIs) {
                distPointBuilder.addDistributionPoint(uri);
            }

            builder.addExtension(X509Extension.cRLDistributionPoints, false /* not critical */,
                    distPointBuilder.buildCRLDistPoint());
        }

        return builder;
    }

    protected X509v1CertificateBuilder createX509v1CertificateBuilder(X509Certificate issuerCertificate)
            throws IOException {
        X500Principal issuerPrincipal = getIssuer();

        if (issuerCertificate != null) {
            issuerPrincipal = issuerCertificate.getSubjectX500Principal();
        }

        X509v1CertificateBuilder builder = new X509v1CertificateBuilder(
                X500PrincipalUtils.toX500Name(issuerPrincipal), serialNumber, notBefore, notAfter,
                X500PrincipalUtils.toX500Name(subject), SubjectPublicKeyInfo.getInstance(publicKey.getEncoded()));

        /*
         *  X509 V1 certificates do not support extensions
         */
        return builder;
    }

    private ContentSigner getContentSigner(PrivateKey privateKey) throws OperatorCreationException {
        JcaContentSignerBuilder contentSignerBuilder = new JcaContentSignerBuilder(signatureAlgorithm);

        contentSignerBuilder.setProvider(signingProvider);

        return contentSignerBuilder.build(privateKey);
    }

    private X509Certificate getCertificate(X509CertificateHolder holder) throws CertificateException {
        JcaX509CertificateConverter converter = new JcaX509CertificateConverter();

        converter.setProvider(certificateProvider);

        return converter.getCertificate(holder);
    }

    protected X509Certificate generateV3CertificateInternal(PrivateKey issuerPrivateKey,
            X509Certificate issuercertificate) throws CertificateBuilderException {
        try {
            X509v3CertificateBuilder builder = createX509v3CertificateBuilder(issuercertificate);

            return getCertificate(builder.build(getContentSigner(issuerPrivateKey)));
        } catch (CertificateParsingException e) {
            throw new CertificateBuilderException(e);
        } catch (IOException e) {
            throw new CertificateBuilderException(e);
        } catch (CertificateException e) {
            throw new CertificateBuilderException(e);
        } catch (OperatorCreationException e) {
            throw new CertificateBuilderException(e);
        } catch (NoSuchAlgorithmException e) {
            throw new CertificateBuilderException(e);
        }
    }

    protected X509Certificate generateV1CertificateInternal(PrivateKey issuerPrivateKey,
            X509Certificate issuercertificate) throws CertificateBuilderException {
        try {
            X509v1CertificateBuilder builder = createX509v1CertificateBuilder(issuercertificate);

            return getCertificate(builder.build(getContentSigner(issuerPrivateKey)));
        } catch (CertificateParsingException e) {
            throw new CertificateBuilderException(e);
        } catch (IOException e) {
            throw new CertificateBuilderException(e);
        } catch (CertificateException e) {
            throw new CertificateBuilderException(e);
        } catch (OperatorCreationException e) {
            throw new CertificateBuilderException(e);
        }
    }

    protected X509Certificate generateCertificateInternal(PrivateKey issuerPrivateKey,
            X509Certificate issuerCertificate) throws CertificateBuilderException {
        return getVersion() == CertificateVersion.V1
                ? generateV1CertificateInternal(issuerPrivateKey, issuerCertificate)
                : generateV3CertificateInternal(issuerPrivateKey, issuerCertificate);
    }

    @Override
    public X509Certificate generateCertificate(PrivateKey issuerPrivateKey, X509Certificate issuerCertificate)
            throws CertificateBuilderException {
        X509Certificate certificate = generateCertificateInternal(issuerPrivateKey, issuerCertificate);

        return certificate;
    }
}