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