1 package org.bouncycastle.cert;
2 
3 import java.io.IOException;
4 import java.math.BigInteger;
5 import java.util.Date;
6 import java.util.Enumeration;
7 import java.util.Locale;
8 
9 import org.bouncycastle.asn1.ASN1Encodable;
10 import org.bouncycastle.asn1.ASN1Encoding;
11 import org.bouncycastle.asn1.ASN1GeneralizedTime;
12 import org.bouncycastle.asn1.ASN1Integer;
13 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
14 import org.bouncycastle.asn1.ASN1Sequence;
15 import org.bouncycastle.asn1.x500.X500Name;
16 import org.bouncycastle.asn1.x509.Extension;
17 import org.bouncycastle.asn1.x509.Extensions;
18 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
19 import org.bouncycastle.asn1.x509.TBSCertList;
20 import org.bouncycastle.asn1.x509.Time;
21 import org.bouncycastle.asn1.x509.V2TBSCertListGenerator;
22 import org.bouncycastle.operator.ContentSigner;
23 
24 /**
25  * class to produce an X.509 Version 2 CRL.
26  */
27 public class X509v2CRLBuilder
28 {
29     private V2TBSCertListGenerator      tbsGen;
30     private ExtensionsGenerator         extGenerator;
31 
32     /**
33      * Basic constructor.
34      *
35      * @param issuer the issuer this CRL is associated with.
36      * @param thisUpdate  the date of this update.
37      */
X509v2CRLBuilder( X500Name issuer, Date thisUpdate)38     public X509v2CRLBuilder(
39         X500Name issuer,
40         Date     thisUpdate)
41     {
42         tbsGen = new V2TBSCertListGenerator();
43         extGenerator = new ExtensionsGenerator();
44 
45         tbsGen.setIssuer(issuer);
46         tbsGen.setThisUpdate(new Time(thisUpdate));
47     }
48 
49     /**
50      * Basic constructor with Locale. You may need to use this constructor if the default locale
51      * doesn't use a Gregorian calender so that the Time produced is compatible with other ASN.1 implementations.
52      *
53      * @param issuer the issuer this CRL is associated with.
54      * @param thisUpdate  the date of this update.
55      * @param dateLocale locale to be used for date interpretation.
56      */
X509v2CRLBuilder( X500Name issuer, Date thisUpdate, Locale dateLocale)57     public X509v2CRLBuilder(
58         X500Name issuer,
59         Date     thisUpdate,
60         Locale   dateLocale)
61     {
62         tbsGen = new V2TBSCertListGenerator();
63         extGenerator = new ExtensionsGenerator();
64 
65         tbsGen.setIssuer(issuer);
66         tbsGen.setThisUpdate(new Time(thisUpdate, dateLocale));
67     }
68 
69     /**
70      * Basic constructor.
71      *
72      * @param issuer the issuer this CRL is associated with.
73      * @param thisUpdate  the Time of this update.
74      */
X509v2CRLBuilder( X500Name issuer, Time thisUpdate)75     public X509v2CRLBuilder(
76         X500Name issuer,
77         Time     thisUpdate)
78     {
79         tbsGen = new V2TBSCertListGenerator();
80         extGenerator = new ExtensionsGenerator();
81 
82         tbsGen.setIssuer(issuer);
83         tbsGen.setThisUpdate(thisUpdate);
84     }
85 
86     /**
87      * Create a builder for a version 2 CRL, initialised with another CRL.
88      *
89      * @param template template CRL to base the new one on.
90      */
X509v2CRLBuilder(X509CRLHolder template)91     public X509v2CRLBuilder(X509CRLHolder template)
92     {
93         tbsGen = new V2TBSCertListGenerator();
94         tbsGen.setIssuer(template.getIssuer());
95         tbsGen.setThisUpdate(new Time(template.getThisUpdate()));
96         Date nextUpdate = template.getNextUpdate();
97         if (nextUpdate != null)
98         {
99             tbsGen.setNextUpdate(new Time(nextUpdate));
100         }
101 
102         addCRL(template);
103 
104         extGenerator = new ExtensionsGenerator();
105 
106         Extensions exts = template.getExtensions();
107         if (exts != null)
108         {
109             for (Enumeration en = exts.oids(); en.hasMoreElements(); )
110             {
111                 extGenerator.addExtension(exts.getExtension((ASN1ObjectIdentifier)en.nextElement()));
112             }
113         }
114     }
115 
116     /**
117      * Return if the extension indicated by OID is present.
118      *
119      * @param oid the OID for the extension of interest.
120      * @return the Extension, or null if it is not present.
121      */
hasExtension(ASN1ObjectIdentifier oid)122     public boolean hasExtension(ASN1ObjectIdentifier oid)
123     {
124          return doGetExtension(oid) != null;
125     }
126 
127     /**
128      * Return the current value of the extension for OID.
129      *
130      * @param oid the OID for the extension we want to fetch.
131      * @return true if a matching extension is present, false otherwise.
132      */
getExtension(ASN1ObjectIdentifier oid)133     public Extension getExtension(ASN1ObjectIdentifier oid)
134     {
135          return doGetExtension(oid);
136     }
137 
doGetExtension(ASN1ObjectIdentifier oid)138     private Extension doGetExtension(ASN1ObjectIdentifier oid)
139     {
140         Extensions exts = extGenerator.generate();
141 
142         return exts.getExtension(oid);
143     }
144 
145     /**
146      * Set the date by which the next CRL will become available.
147      *
148      * @param date  date of next CRL update.
149      * @return the current builder.
150      */
setNextUpdate( Date date)151     public X509v2CRLBuilder setNextUpdate(
152         Date    date)
153     {
154         return this.setNextUpdate(new Time(date));
155     }
156 
157     /**
158      * Set the date by which the next CRL will become available.
159      *
160      * @param date  date of next CRL update.
161      * @param dateLocale locale to be used for date interpretation.
162      * @return the current builder.
163      */
setNextUpdate( Date date, Locale dateLocale)164     public X509v2CRLBuilder setNextUpdate(
165         Date    date,
166         Locale  dateLocale)
167     {
168         return this.setNextUpdate(new Time(date, dateLocale));
169     }
170 
171     /**
172      * Set the date by which the next CRL will become available.
173      *
174      * @param date  date of next CRL update.
175      * @return the current builder.
176      */
setNextUpdate( Time date)177     public X509v2CRLBuilder setNextUpdate(
178         Time    date)
179     {
180         tbsGen.setNextUpdate(date);
181 
182         return this;
183     }
184 
185     /**
186      * Add a CRL entry with the just reasonCode extension.
187      *
188      * @param userCertificateSerial serial number of revoked certificate.
189      * @param revocationDate date of certificate revocation.
190      * @param reason the reason code, as indicated in CRLReason, i.e CRLReason.keyCompromise, or 0 if not to be used.
191      * @return the current builder.
192      */
addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, int reason)193     public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, int reason)
194     {
195         tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), reason);
196 
197         return this;
198     }
199 
200     /**
201      * Add a CRL entry with an invalidityDate extension as well as a reasonCode extension. This is used
202      * where the date of revocation might be after issues with the certificate may have occurred.
203      *
204      * @param userCertificateSerial serial number of revoked certificate.
205      * @param revocationDate date of certificate revocation.
206      * @param reason the reason code, as indicated in CRLReason, i.e CRLReason.keyCompromise, or 0 if not to be used.
207      * @param invalidityDate the date on which the private key for the certificate became compromised or the certificate otherwise became invalid.
208      * @return the current builder.
209      */
addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, int reason, Date invalidityDate)210     public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, int reason, Date invalidityDate)
211     {
212         tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), reason, new ASN1GeneralizedTime(invalidityDate));
213 
214         return this;
215     }
216 
217     /**
218      * Add a CRL entry with extensions.
219      *
220      * @param userCertificateSerial serial number of revoked certificate.
221      * @param revocationDate date of certificate revocation.
222      * @param extensions extension set to be associated with this CRLEntry.
223      * @return the current builder.
224      */
addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, Extensions extensions)225     public X509v2CRLBuilder addCRLEntry(BigInteger userCertificateSerial, Date revocationDate, Extensions extensions)
226     {
227         tbsGen.addCRLEntry(new ASN1Integer(userCertificateSerial), new Time(revocationDate), extensions);
228 
229         return this;
230     }
231 
232     /**
233      * Add the CRLEntry objects contained in a previous CRL.
234      *
235      * @param other the X509CRLHolder to source the other entries from.
236      * @return the current builder.
237      */
addCRL(X509CRLHolder other)238     public X509v2CRLBuilder addCRL(X509CRLHolder other)
239     {
240         TBSCertList revocations = other.toASN1Structure().getTBSCertList();
241 
242         if (revocations != null)
243         {
244             for (Enumeration en = revocations.getRevokedCertificateEnumeration(); en.hasMoreElements();)
245             {
246                 tbsGen.addCRLEntry(ASN1Sequence.getInstance(((ASN1Encodable)en.nextElement()).toASN1Primitive()));
247             }
248         }
249 
250         return this;
251     }
252 
253     /**
254      * Add a given extension field for the standard extensions tag (tag 3)
255      *
256      * @param oid the OID defining the extension type.
257      * @param isCritical true if the extension is critical, false otherwise.
258      * @param value the ASN.1 structure that forms the extension's value.
259      * @return this builder object.
260      */
addExtension( ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value)261     public X509v2CRLBuilder addExtension(
262         ASN1ObjectIdentifier oid,
263         boolean isCritical,
264         ASN1Encodable value)
265         throws CertIOException
266     {
267         CertUtils.addExtension(extGenerator, oid, isCritical, value);
268 
269         return this;
270     }
271 
272     /**
273      * Add a given extension field for the standard extensions tag (tag 3) using a byte encoding of the
274      * extension value.
275      *
276      * @param oid the OID defining the extension type.
277      * @param isCritical true if the extension is critical, false otherwise.
278      * @param encodedValue a byte array representing the encoding of the extension value.
279      * @return this builder object.
280      */
addExtension( ASN1ObjectIdentifier oid, boolean isCritical, byte[] encodedValue)281     public X509v2CRLBuilder addExtension(
282         ASN1ObjectIdentifier oid,
283         boolean isCritical,
284         byte[] encodedValue)
285         throws CertIOException
286     {
287         extGenerator.addExtension(oid, isCritical, encodedValue);
288 
289         return this;
290     }
291 
292     /**
293      * Add a given extension field for the standard extensions tag (tag 3).
294      *
295      * @param extension the full extension value.
296      * @return this builder object.
297      */
addExtension( Extension extension)298     public X509v2CRLBuilder addExtension(
299         Extension extension)
300         throws CertIOException
301     {
302         extGenerator.addExtension(extension);
303 
304         return this;
305     }
306 
307     /**
308      * Replace the extension field for the passed in extension's extension ID
309      * with a new version.
310      *
311      * @param oid the OID defining the extension type.
312      * @param isCritical true if the extension is critical, false otherwise.
313      * @param value the ASN.1 structure that forms the extension's value.
314      * @return this builder object.
315      * @throws CertIOException if there is an issue with the new extension value.
316      * @throws IllegalArgumentException if the extension to be replaced is not present.
317      */
replaceExtension( ASN1ObjectIdentifier oid, boolean isCritical, ASN1Encodable value)318     public X509v2CRLBuilder replaceExtension(
319         ASN1ObjectIdentifier oid,
320         boolean isCritical,
321         ASN1Encodable value)
322         throws CertIOException
323     {
324         try
325         {
326             extGenerator = CertUtils.doReplaceExtension(extGenerator, new Extension(oid, isCritical, value.toASN1Primitive().getEncoded(ASN1Encoding.DER)));
327         }
328         catch (IOException e)
329         {
330             throw new CertIOException("cannot encode extension: " + e.getMessage(), e);
331         }
332 
333         return this;
334     }
335 
336     /**
337      * Replace the extension field for the passed in extension's extension ID
338      * with a new version.
339      *
340      * @param extension the full extension value.
341      * @return this builder object.
342      * @throws CertIOException if there is an issue with the new extension value.
343      * @throws IllegalArgumentException if the extension to be replaced is not present.
344      */
replaceExtension( Extension extension)345     public X509v2CRLBuilder replaceExtension(
346         Extension extension)
347         throws CertIOException
348     {
349         extGenerator = CertUtils.doReplaceExtension(extGenerator, extension);
350 
351         return this;
352     }
353 
354     /**
355      * Replace a given extension field for the standard extensions tag (tag 3) with the passed in
356      * byte encoded extension value.
357      *
358      * @param oid the OID defining the extension type.
359      * @param isCritical true if the extension is critical, false otherwise.
360      * @param encodedValue a byte array representing the encoding of the extension value.
361      * @return this builder object.
362      * @throws CertIOException if there is an issue with the new extension value.
363      * @throws IllegalArgumentException if the extension to be replaced is not present.
364      */
replaceExtension( ASN1ObjectIdentifier oid, boolean isCritical, byte[] encodedValue)365     public X509v2CRLBuilder replaceExtension(
366         ASN1ObjectIdentifier oid,
367         boolean isCritical,
368         byte[] encodedValue)
369         throws CertIOException
370     {
371         extGenerator = CertUtils.doReplaceExtension(extGenerator, new Extension(oid, isCritical, encodedValue));
372 
373         return this;
374     }
375 
376     /**
377      * Remove the extension indicated by OID.
378      *
379      * @param oid the OID of the extension to be removed.
380      * @return this builder object.
381      * @throws IllegalArgumentException if the extension to be removed is not present.
382      */
removeExtension(ASN1ObjectIdentifier oid)383     public X509v2CRLBuilder removeExtension(ASN1ObjectIdentifier oid)
384     {
385         extGenerator = CertUtils.doRemoveExtension(extGenerator, oid);
386 
387         return this;
388     }
389 
390     /**
391      * Generate an X.509 CRL, based on the current issuer and subject
392      * using the passed in signer.
393      *
394      * @param signer the content signer to be used to generate the signature validating the certificate.
395      * @return a holder containing the resulting signed certificate.
396      */
build( ContentSigner signer)397     public X509CRLHolder build(
398         ContentSigner signer)
399     {
400         tbsGen.setSignature(signer.getAlgorithmIdentifier());
401 
402         if (!extGenerator.isEmpty())
403         {
404             tbsGen.setExtensions(extGenerator.generate());
405         }
406 
407         return CertUtils.generateFullCRL(signer, tbsGen.generateTBSCertList());
408     }
409 }
410