Java tutorial
/* * 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; } }