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