X509V3CertificateGenerator.java revision c415feea05de0907cf741293bd538bd10d3194c6
1package org.bouncycastle.x509;
2
3import java.io.IOException;
4import java.math.BigInteger;
5import java.security.GeneralSecurityException;
6import java.security.InvalidKeyException;
7import java.security.NoSuchAlgorithmException;
8import java.security.NoSuchProviderException;
9import java.security.PrivateKey;
10import java.security.PublicKey;
11import java.security.SecureRandom;
12import java.security.SignatureException;
13import java.security.cert.CertificateEncodingException;
14import java.security.cert.CertificateParsingException;
15import java.security.cert.X509Certificate;
16import java.util.Date;
17import java.util.Iterator;
18
19import javax.security.auth.x500.X500Principal;
20
21import org.bouncycastle.asn1.ASN1Encodable;
22import org.bouncycastle.asn1.ASN1EncodableVector;
23import org.bouncycastle.asn1.ASN1InputStream;
24import org.bouncycastle.asn1.ASN1Integer;
25import org.bouncycastle.asn1.ASN1ObjectIdentifier;
26import org.bouncycastle.asn1.DERBitString;
27import org.bouncycastle.asn1.DERSequence;
28import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
29import org.bouncycastle.asn1.x509.Certificate;
30import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
31import org.bouncycastle.asn1.x509.TBSCertificate;
32import org.bouncycastle.asn1.x509.Time;
33import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator;
34import org.bouncycastle.asn1.x509.X509ExtensionsGenerator;
35import org.bouncycastle.asn1.x509.X509Name;
36import org.bouncycastle.jce.X509Principal;
37// BEGIN ANDROID-ADDED
38// See the definition of the jcaJceHelper field for details.
39import org.bouncycastle.jcajce.provider.asymmetric.x509.X509CertificateObject;
40import org.bouncycastle.jcajce.util.BCJcaJceHelper;
41import org.bouncycastle.jcajce.util.JcaJceHelper;
42// END ANDROID-ADDED
43import org.bouncycastle.x509.extension.X509ExtensionUtil;
44
45/**
46 * class to produce an X.509 Version 3 certificate.
47 *  @deprecated use org.bouncycastle.cert.X509v3CertificateBuilder.
48 */
49public class X509V3CertificateGenerator
50{
51    private V3TBSCertificateGenerator   tbsGen;
52    private ASN1ObjectIdentifier        sigOID;
53    private AlgorithmIdentifier         sigAlgId;
54    private String                      signatureAlgorithm;
55    private X509ExtensionsGenerator     extGenerator;
56    // BEGIN ANDROID-ADDED
57    // Use org.bouncycastle.jcajce.provider.asymmetric.x509.X509CertificateObject
58    // instead of org.bouncycastle.jce.provider.X509CertificateObject.
59    // We need to pass one instance of JcaJceHelper in the constructor of the former class.
60    private final JcaJceHelper jcaJceHelper = new BCJcaJceHelper();
61    // END ANDROID-ADDED
62
63    public X509V3CertificateGenerator()
64    {
65        tbsGen = new V3TBSCertificateGenerator();
66        extGenerator = new X509ExtensionsGenerator();
67    }
68
69    /**
70     * reset the generator
71     */
72    public void reset()
73    {
74        tbsGen = new V3TBSCertificateGenerator();
75        extGenerator.reset();
76    }
77
78    /**
79     * set the serial number for the certificate.
80     */
81    public void setSerialNumber(
82        BigInteger      serialNumber)
83    {
84        if (serialNumber.compareTo(BigInteger.ZERO) <= 0)
85        {
86            throw new IllegalArgumentException("serial number must be a positive integer");
87        }
88
89        tbsGen.setSerialNumber(new ASN1Integer(serialNumber));
90    }
91
92    /**
93     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
94     * certificate.
95     */
96    public void setIssuerDN(
97        X500Principal   issuer)
98    {
99        try
100        {
101            tbsGen.setIssuer(new X509Principal(issuer.getEncoded()));
102        }
103        catch (IOException e)
104        {
105            throw new IllegalArgumentException("can't process principal: " + e);
106        }
107    }
108
109    /**
110     * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the
111     * certificate.
112     */
113    public void setIssuerDN(
114        X509Name   issuer)
115    {
116        tbsGen.setIssuer(issuer);
117    }
118
119    public void setNotBefore(
120        Date    date)
121    {
122        tbsGen.setStartDate(new Time(date));
123    }
124
125    public void setNotAfter(
126        Date    date)
127    {
128        tbsGen.setEndDate(new Time(date));
129    }
130
131    /**
132     * Set the subject distinguished name. The subject describes the entity associated with the public key.
133     */
134    public void setSubjectDN(
135        X500Principal   subject)
136    {
137        try
138        {
139            tbsGen.setSubject(new X509Principal(subject.getEncoded()));
140        }
141        catch (IOException e)
142        {
143            throw new IllegalArgumentException("can't process principal: " + e);
144        }
145    }
146
147    /**
148     * Set the subject distinguished name. The subject describes the entity associated with the public key.
149     */
150    public void setSubjectDN(
151        X509Name   subject)
152    {
153        tbsGen.setSubject(subject);
154    }
155
156    public void setPublicKey(
157        PublicKey       key)
158        throws IllegalArgumentException
159    {
160        try
161        {
162            tbsGen.setSubjectPublicKeyInfo(
163                       SubjectPublicKeyInfo.getInstance(new ASN1InputStream(key.getEncoded()).readObject()));
164        }
165        catch (Exception e)
166        {
167            throw new IllegalArgumentException("unable to process key - " + e.toString());
168        }
169    }
170
171    /**
172     * Set the signature algorithm. This can be either a name or an OID, names
173     * are treated as case insensitive.
174     *
175     * @param signatureAlgorithm string representation of the algorithm name.
176     */
177    public void setSignatureAlgorithm(
178        String  signatureAlgorithm)
179    {
180        this.signatureAlgorithm = signatureAlgorithm;
181
182        try
183        {
184            sigOID = X509Util.getAlgorithmOID(signatureAlgorithm);
185        }
186        catch (Exception e)
187        {
188            throw new IllegalArgumentException("Unknown signature type requested: " + signatureAlgorithm);
189        }
190
191        sigAlgId = X509Util.getSigAlgID(sigOID, signatureAlgorithm);
192
193        tbsGen.setSignature(sigAlgId);
194    }
195
196    /**
197     * Set the subject unique ID - note: it is very rare that it is correct to do this.
198     */
199    public void setSubjectUniqueID(boolean[] uniqueID)
200    {
201        tbsGen.setSubjectUniqueID(booleanToBitString(uniqueID));
202    }
203
204    /**
205     * Set the issuer unique ID - note: it is very rare that it is correct to do this.
206     */
207    public void setIssuerUniqueID(boolean[] uniqueID)
208    {
209        tbsGen.setIssuerUniqueID(booleanToBitString(uniqueID));
210    }
211
212    private DERBitString booleanToBitString(boolean[] id)
213    {
214        byte[] bytes = new byte[(id.length + 7) / 8];
215
216        for (int i = 0; i != id.length; i++)
217        {
218            bytes[i / 8] |= (id[i]) ? (1 << ((7 - (i % 8)))) : 0;
219        }
220
221        int pad = id.length % 8;
222
223        if (pad == 0)
224        {
225            return new DERBitString(bytes);
226        }
227        else
228        {
229            return new DERBitString(bytes, 8 - pad);
230        }
231    }
232
233    /**
234     * add a given extension field for the standard extensions tag (tag 3)
235     */
236    public void addExtension(
237        String          oid,
238        boolean         critical,
239        ASN1Encodable    value)
240    {
241        this.addExtension(new ASN1ObjectIdentifier(oid), critical, value);
242    }
243
244    /**
245     * add a given extension field for the standard extensions tag (tag 3)
246     */
247    public void addExtension(
248        ASN1ObjectIdentifier oid,
249        boolean             critical,
250        ASN1Encodable        value)
251    {
252        extGenerator.addExtension(new ASN1ObjectIdentifier(oid.getId()), critical,  value);
253    }
254
255    /**
256     * add a given extension field for the standard extensions tag (tag 3)
257     * The value parameter becomes the contents of the octet string associated
258     * with the extension.
259     */
260    public void addExtension(
261        String          oid,
262        boolean         critical,
263        byte[]          value)
264    {
265        this.addExtension(new ASN1ObjectIdentifier(oid), critical, value);
266    }
267
268    /**
269     * add a given extension field for the standard extensions tag (tag 3)
270     */
271    public void addExtension(
272        ASN1ObjectIdentifier oid,
273        boolean             critical,
274        byte[]              value)
275    {
276        extGenerator.addExtension(new ASN1ObjectIdentifier(oid.getId()), critical, value);
277    }
278
279    /**
280     * add a given extension field for the standard extensions tag (tag 3)
281     * copying the extension value from another certificate.
282     * @throws CertificateParsingException if the extension cannot be extracted.
283     */
284    public void copyAndAddExtension(
285        String          oid,
286        boolean         critical,
287        X509Certificate cert)
288        throws CertificateParsingException
289    {
290        byte[] extValue = cert.getExtensionValue(oid);
291
292        if (extValue == null)
293        {
294            throw new CertificateParsingException("extension " + oid + " not present");
295        }
296
297        try
298        {
299            ASN1Encodable value = X509ExtensionUtil.fromExtensionValue(extValue);
300
301            this.addExtension(oid, critical, value);
302        }
303        catch (IOException e)
304        {
305            throw new CertificateParsingException(e.toString());
306        }
307    }
308
309    /**
310     * add a given extension field for the standard extensions tag (tag 3)
311     * copying the extension value from another certificate.
312     * @throws CertificateParsingException if the extension cannot be extracted.
313     */
314    public void copyAndAddExtension(
315        ASN1ObjectIdentifier oid,
316        boolean             critical,
317        X509Certificate     cert)
318        throws CertificateParsingException
319    {
320        this.copyAndAddExtension(oid.getId(), critical, cert);
321    }
322
323    /**
324     * generate an X509 certificate, based on the current issuer and subject
325     * using the default provider "BC".
326     * @deprecated use generate(key, "BC")
327     */
328    public X509Certificate generateX509Certificate(
329        PrivateKey      key)
330        throws SecurityException, SignatureException, InvalidKeyException
331    {
332        try
333        {
334            return generateX509Certificate(key, "BC", null);
335        }
336        catch (NoSuchProviderException e)
337        {
338            throw new SecurityException("BC provider not installed!");
339        }
340    }
341
342    /**
343     * generate an X509 certificate, based on the current issuer and subject
344     * using the default provider "BC", and the passed in source of randomness
345     * (if required).
346     * @deprecated use generate(key, random, "BC")
347     */
348    public X509Certificate generateX509Certificate(
349        PrivateKey      key,
350        SecureRandom    random)
351        throws SecurityException, SignatureException, InvalidKeyException
352    {
353        try
354        {
355            return generateX509Certificate(key, "BC", random);
356        }
357        catch (NoSuchProviderException e)
358        {
359            throw new SecurityException("BC provider not installed!");
360        }
361    }
362
363    /**
364     * generate an X509 certificate, based on the current issuer and subject,
365     * using the passed in provider for the signing.
366     * @deprecated use generate()
367     */
368    public X509Certificate generateX509Certificate(
369        PrivateKey      key,
370        String          provider)
371        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
372    {
373        return generateX509Certificate(key, provider, null);
374    }
375
376    /**
377     * generate an X509 certificate, based on the current issuer and subject,
378     * using the passed in provider for the signing and the supplied source
379     * of randomness, if required.
380     * @deprecated use generate()
381     */
382    public X509Certificate generateX509Certificate(
383        PrivateKey      key,
384        String          provider,
385        SecureRandom    random)
386        throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException
387    {
388        try
389        {
390            return generate(key, provider, random);
391        }
392        catch (NoSuchProviderException e)
393        {
394            throw e;
395        }
396        catch (SignatureException e)
397        {
398            throw e;
399        }
400        catch (InvalidKeyException e)
401        {
402            throw e;
403        }
404        catch (GeneralSecurityException e)
405        {
406            throw new SecurityException("exception: " + e);
407        }
408    }
409
410    /**
411     * generate an X509 certificate, based on the current issuer and subject
412     * using the default provider.
413     * <p>
414     * <b>Note:</b> this differs from the deprecated method in that the default provider is
415     * used - not "BC".
416     * </p>
417     */
418    public X509Certificate generate(
419        PrivateKey      key)
420        throws CertificateEncodingException, IllegalStateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
421    {
422        return generate(key, (SecureRandom)null);
423    }
424
425    /**
426     * generate an X509 certificate, based on the current issuer and subject
427     * using the default provider, and the passed in source of randomness
428     * (if required).
429     * <p>
430     * <b>Note:</b> this differs from the deprecated method in that the default provider is
431     * used - not "BC".
432     * </p>
433     */
434    public X509Certificate generate(
435        PrivateKey      key,
436        SecureRandom    random)
437        throws CertificateEncodingException, IllegalStateException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
438    {
439        TBSCertificate tbsCert = generateTbsCert();
440        byte[] signature;
441
442        try
443        {
444            signature = X509Util.calculateSignature(sigOID, signatureAlgorithm, key, random, tbsCert);
445        }
446        catch (IOException e)
447        {
448            throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
449        }
450
451        try
452        {
453            return generateJcaObject(tbsCert, signature);
454        }
455        catch (CertificateParsingException e)
456        {
457            throw new ExtCertificateEncodingException("exception producing certificate object", e);
458        }
459    }
460
461    /**
462     * generate an X509 certificate, based on the current issuer and subject,
463     * using the passed in provider for the signing.
464     */
465    public X509Certificate generate(
466        PrivateKey      key,
467        String          provider)
468        throws CertificateEncodingException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
469    {
470        return generate(key, provider, null);
471    }
472
473    /**
474     * generate an X509 certificate, based on the current issuer and subject,
475     * using the passed in provider for the signing and the supplied source
476     * of randomness, if required.
477     */
478    public X509Certificate generate(
479        PrivateKey      key,
480        String          provider,
481        SecureRandom    random)
482        throws CertificateEncodingException, IllegalStateException, NoSuchProviderException, NoSuchAlgorithmException, SignatureException, InvalidKeyException
483    {
484        TBSCertificate tbsCert = generateTbsCert();
485        byte[] signature;
486
487        try
488        {
489            signature = X509Util.calculateSignature(sigOID, signatureAlgorithm, provider, key, random, tbsCert);
490        }
491        catch (IOException e)
492        {
493            throw new ExtCertificateEncodingException("exception encoding TBS cert", e);
494        }
495
496        try
497        {
498            return generateJcaObject(tbsCert, signature);
499        }
500        catch (CertificateParsingException e)
501        {
502            throw new ExtCertificateEncodingException("exception producing certificate object", e);
503        }
504    }
505
506    private TBSCertificate generateTbsCert()
507    {
508        if (!extGenerator.isEmpty())
509        {
510            tbsGen.setExtensions(extGenerator.generate());
511        }
512
513        return tbsGen.generateTBSCertificate();
514    }
515
516    private X509Certificate generateJcaObject(TBSCertificate tbsCert, byte[] signature)
517        throws CertificateParsingException
518    {
519        ASN1EncodableVector v = new ASN1EncodableVector();
520
521        v.add(tbsCert);
522        v.add(sigAlgId);
523        v.add(new DERBitString(signature));
524        // BEGIN ANDROID-CHANGED
525        // Was: return new X509CertificateObject(Certificate.getInstance(new DERSequence(v)));
526        // We are using a different X509CertificateObject class than the original, see definition
527        // of the jcaJceHelper field for details.
528        return new X509CertificateObject(jcaJceHelper, Certificate.getInstance(new DERSequence(v)));
529        // END ANDROID-CHANGED
530    }
531
532    /**
533     * Return an iterator of the signature names supported by the generator.
534     *
535     * @return an iterator containing recognised names.
536     */
537    public Iterator getSignatureAlgNames()
538    {
539        return X509Util.getAlgNames();
540    }
541}
542