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