1package org.bouncycastle.cert;
2
3import java.math.BigInteger;
4import java.util.Date;
5import java.util.Locale;
6
7import org.bouncycastle.asn1.ASN1Encodable;
8import org.bouncycastle.asn1.ASN1Integer;
9import org.bouncycastle.asn1.ASN1ObjectIdentifier;
10import org.bouncycastle.asn1.x500.X500Name;
11import org.bouncycastle.asn1.x509.Certificate;
12import org.bouncycastle.asn1.x509.Extension;
13import org.bouncycastle.asn1.x509.ExtensionsGenerator;
14import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
15import org.bouncycastle.asn1.x509.Time;
16import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
17import org.bouncycastle.operator.ContentSigner;
18
19
20/**
21 * class to produce an X.509 Version 3 certificate.
22 */
23public class X509v3CertificateBuilder
24{
25    private V3TBSCertificateGenerator   tbsGen;
26    private ExtensionsGenerator extGenerator;
27
28    /**
29     * Create a builder for a version 3 certificate.
30     *
31     * @param issuer the certificate issuer
32     * @param serial the certificate serial number
33     * @param notBefore the date before which the certificate is not valid
34     * @param notAfter the date after which the certificate is not valid
35     * @param subject the certificate subject
36     * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
37     */
38    public X509v3CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
39    {
40        this(issuer, serial, new Time(notBefore), new Time(notAfter), subject, publicKeyInfo);
41    }
42
43    /**
44     * Create a builder for a version 3 certificate. You may need to use this constructor if the default locale
45     * doesn't use a Gregorian calender so that the Time produced is compatible with other ASN.1 implementations.
46     *
47     * @param issuer the certificate issuer
48     * @param serial the certificate serial number
49     * @param notBefore the date before which the certificate is not valid
50     * @param notAfter the date after which the certificate is not valid
51     * @param dateLocale locale to be used for date interpretation.
52     * @param subject the certificate subject
53     * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
54     */
55    public X509v3CertificateBuilder(X500Name issuer, BigInteger serial, Date notBefore, Date notAfter, Locale dateLocale, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
56    {
57        this(issuer, serial, new Time(notBefore, dateLocale), new Time(notAfter, dateLocale), subject, publicKeyInfo);
58    }
59
60    /**
61     * Create a builder for a version 3 certificate.
62     *
63     * @param issuer the certificate issuer
64     * @param serial the certificate serial number
65     * @param notBefore the Time before which the certificate is not valid
66     * @param notAfter the Time after which the certificate is not valid
67     * @param subject the certificate subject
68     * @param publicKeyInfo the info structure for the public key to be associated with this certificate.
69     */
70    public X509v3CertificateBuilder(X500Name issuer, BigInteger serial, Time notBefore, Time notAfter, X500Name subject, SubjectPublicKeyInfo publicKeyInfo)
71    {
72        tbsGen = new V3TBSCertificateGenerator();
73        tbsGen.setSerialNumber(new ASN1Integer(serial));
74        tbsGen.setIssuer(issuer);
75        tbsGen.setStartDate(notBefore);
76        tbsGen.setEndDate(notAfter);
77        tbsGen.setSubject(subject);
78        tbsGen.setSubjectPublicKeyInfo(publicKeyInfo);
79
80        extGenerator = new ExtensionsGenerator();
81    }
82
83    /**
84     * Set the subjectUniqueID - note: it is very rare that it is correct to do this.
85     *
86     * @param uniqueID a boolean array representing the bits making up the subjectUniqueID.
87     * @return this builder object.
88     */
89    public X509v3CertificateBuilder setSubjectUniqueID(boolean[] uniqueID)
90    {
91        tbsGen.setSubjectUniqueID(CertUtils.booleanToBitString(uniqueID));
92
93        return this;
94    }
95
96    /**
97     * Set the issuerUniqueID - note: it is very rare that it is correct to do this.
98     *
99     * @param uniqueID a boolean array representing the bits making up the issuerUniqueID.
100     * @return this builder object.
101     */
102    public X509v3CertificateBuilder setIssuerUniqueID(boolean[] uniqueID)
103    {
104        tbsGen.setIssuerUniqueID(CertUtils.booleanToBitString(uniqueID));
105
106        return this;
107    }
108
109    /**
110     * Add a given extension field for the standard extensions tag (tag 3)
111     *
112     * @param oid the OID defining the extension type.
113     * @param isCritical true if the extension is critical, false otherwise.
114     * @param value the ASN.1 structure that forms the extension's value.
115     * @return this builder object.
116     */
117    public X509v3CertificateBuilder addExtension(
118        ASN1ObjectIdentifier oid,
119        boolean isCritical,
120        ASN1Encodable value)
121        throws CertIOException
122    {
123        CertUtils.addExtension(extGenerator, oid, isCritical, value);
124
125        return this;
126    }
127
128    /**
129     * Add a given extension field for the standard extensions tag (tag 3).
130     *
131     * @param extension the full extension value.
132     * @return this builder object.
133     */
134    public X509v3CertificateBuilder addExtension(
135        Extension extension)
136        throws CertIOException
137    {
138        extGenerator.addExtension(extension);
139
140        return this;
141    }
142
143    /**
144     * Add a given extension field for the standard extensions tag (tag 3) using a byte encoding of the
145     * extension value.
146     *
147     * @param oid the OID defining the extension type.
148     * @param isCritical true if the extension is critical, false otherwise.
149     * @param encodedValue a byte array representing the encoding of the extension value.
150     * @return this builder object.
151     */
152    public X509v3CertificateBuilder addExtension(
153        ASN1ObjectIdentifier oid,
154        boolean isCritical,
155        byte[] encodedValue)
156        throws CertIOException
157    {
158        extGenerator.addExtension(oid, isCritical, encodedValue);
159
160        return this;
161    }
162
163    /**
164     * Add a given extension field for the standard extensions tag (tag 3)
165     * copying the extension value from another certificate.
166     *
167     * @param oid the OID defining the extension type.
168     * @param isCritical true if the copied extension is to be marked as critical, false otherwise.
169     * @param certHolder the holder for the certificate that the extension is to be copied from.
170     * @return this builder object.
171     */
172    public X509v3CertificateBuilder copyAndAddExtension(
173        ASN1ObjectIdentifier oid,
174        boolean isCritical,
175        X509CertificateHolder certHolder)
176    {
177        Certificate cert = certHolder.toASN1Structure();
178
179        Extension extension = cert.getTBSCertificate().getExtensions().getExtension(oid);
180
181        if (extension == null)
182        {
183            throw new NullPointerException("extension " + oid + " not present");
184        }
185
186        extGenerator.addExtension(oid, isCritical, extension.getExtnValue().getOctets());
187
188        return this;
189    }
190
191    /**
192     * Generate an X.509 certificate, based on the current issuer and subject
193     * using the passed in signer.
194     *
195     * @param signer the content signer to be used to generate the signature validating the certificate.
196     * @return a holder containing the resulting signed certificate.
197     */
198    public X509CertificateHolder build(
199        ContentSigner signer)
200    {
201        tbsGen.setSignature(signer.getAlgorithmIdentifier());
202
203        if (!extGenerator.isEmpty())
204        {
205            tbsGen.setExtensions(extGenerator.generate());
206        }
207
208        return CertUtils.generateFullCert(signer, tbsGen.generateTBSCertificate());
209    }
210}