1package org.bouncycastle.x509; 2 3import java.io.ByteArrayInputStream; 4import java.io.ByteArrayOutputStream; 5import java.io.IOException; 6import java.math.BigInteger; 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.Signature; 14import java.security.SignatureException; 15import java.security.cert.CertificateParsingException; 16import java.security.cert.X509Certificate; 17import java.util.Date; 18import java.util.Hashtable; 19import java.util.Iterator; 20import java.util.Vector; 21 22import javax.security.auth.x500.X500Principal; 23 24import org.bouncycastle.asn1.*; 25import org.bouncycastle.asn1.x509.AlgorithmIdentifier; 26import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; 27import org.bouncycastle.asn1.x509.TBSCertificateStructure; 28import org.bouncycastle.asn1.x509.Time; 29import org.bouncycastle.asn1.x509.V3TBSCertificateGenerator; 30import org.bouncycastle.asn1.x509.X509CertificateStructure; 31import org.bouncycastle.asn1.x509.X509Extension; 32import org.bouncycastle.asn1.x509.X509Extensions; 33import org.bouncycastle.asn1.x509.X509Name; 34import org.bouncycastle.jce.X509Principal; 35import org.bouncycastle.jce.provider.X509CertificateObject; 36import org.bouncycastle.x509.extension.X509ExtensionUtil; 37 38/** 39 * class to produce an X.509 Version 3 certificate. 40 */ 41public class X509V3CertificateGenerator 42{ 43 private V3TBSCertificateGenerator tbsGen; 44 private DERObjectIdentifier sigOID; 45 private AlgorithmIdentifier sigAlgId; 46 private String signatureAlgorithm; 47 private Hashtable extensions = null; 48 private Vector extOrdering = null; 49 50 public X509V3CertificateGenerator() 51 { 52 tbsGen = new V3TBSCertificateGenerator(); 53 } 54 55 /** 56 * reset the generator 57 */ 58 public void reset() 59 { 60 tbsGen = new V3TBSCertificateGenerator(); 61 extensions = null; 62 extOrdering = null; 63 } 64 65 /** 66 * set the serial number for the certificate. 67 */ 68 public void setSerialNumber( 69 BigInteger serialNumber) 70 { 71 if (serialNumber.compareTo(BigInteger.ZERO) <= 0) 72 { 73 throw new IllegalArgumentException("serial number must be a positive integer"); 74 } 75 76 tbsGen.setSerialNumber(new DERInteger(serialNumber)); 77 } 78 79 /** 80 * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the 81 * certificate. 82 */ 83 public void setIssuerDN( 84 X500Principal issuer) 85 { 86 try 87 { 88 tbsGen.setIssuer(new X509Principal(issuer.getEncoded())); 89 } 90 catch (IOException e) 91 { 92 throw new IllegalArgumentException("can't process principal: " + e); 93 } 94 } 95 96 /** 97 * Set the issuer distinguished name - the issuer is the entity whose private key is used to sign the 98 * certificate. 99 */ 100 public void setIssuerDN( 101 X509Name issuer) 102 { 103 tbsGen.setIssuer(issuer); 104 } 105 106 public void setNotBefore( 107 Date date) 108 { 109 tbsGen.setStartDate(new Time(date)); 110 } 111 112 public void setNotAfter( 113 Date date) 114 { 115 tbsGen.setEndDate(new Time(date)); 116 } 117 118 /** 119 * Set the subject distinguished name. The subject describes the entity associated with the public key. 120 */ 121 public void setSubjectDN( 122 X500Principal subject) 123 { 124 try 125 { 126 tbsGen.setSubject(new X509Principal(subject.getEncoded())); 127 } 128 catch (IOException e) 129 { 130 throw new IllegalArgumentException("can't process principal: " + e); 131 } 132 } 133 134 /** 135 * Set the subject distinguished name. The subject describes the entity associated with the public key. 136 */ 137 public void setSubjectDN( 138 X509Name subject) 139 { 140 tbsGen.setSubject(subject); 141 } 142 143 public void setPublicKey( 144 PublicKey key) 145 { 146 try 147 { 148 tbsGen.setSubjectPublicKeyInfo(new SubjectPublicKeyInfo((ASN1Sequence)new ASN1InputStream( 149 new ByteArrayInputStream(key.getEncoded())).readObject())); 150 } 151 catch (Exception e) 152 { 153 throw new IllegalArgumentException("unable to process key - " + e.toString()); 154 } 155 } 156 157 /** 158 * Set the signature algorithm. This can be either a name or an OID, names 159 * are treated as case insensitive. 160 * 161 * @param signatureAlgorithm string representation of the algorithm name. 162 */ 163 public void setSignatureAlgorithm( 164 String signatureAlgorithm) 165 { 166 this.signatureAlgorithm = signatureAlgorithm; 167 168 try 169 { 170 sigOID = X509Util.getAlgorithmOID(signatureAlgorithm); 171 } 172 catch (Exception e) 173 { 174 throw new IllegalArgumentException("Unknown signature type requested: " + signatureAlgorithm); 175 } 176 177 sigAlgId = X509Util.getSigAlgID(sigOID); 178 179 tbsGen.setSignature(sigAlgId); 180 } 181 182 /** 183 * add a given extension field for the standard extensions tag (tag 3) 184 */ 185 public void addExtension( 186 String oid, 187 boolean critical, 188 DEREncodable value) 189 { 190 this.addExtension(new DERObjectIdentifier(oid), critical, value); 191 } 192 193 /** 194 * add a given extension field for the standard extensions tag (tag 3) 195 */ 196 public void addExtension( 197 DERObjectIdentifier oid, 198 boolean critical, 199 DEREncodable value) 200 { 201 if (extensions == null) 202 { 203 extensions = new Hashtable(); 204 extOrdering = new Vector(); 205 } 206 207 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 208 DEROutputStream dOut = new DEROutputStream(bOut); 209 210 try 211 { 212 dOut.writeObject(value); 213 } 214 catch (IOException e) 215 { 216 throw new IllegalArgumentException("error encoding value: " + e); 217 } 218 219 this.addExtension(oid, critical, bOut.toByteArray()); 220 } 221 222 /** 223 * add a given extension field for the standard extensions tag (tag 3) 224 * The value parameter becomes the contents of the octet string associated 225 * with the extension. 226 */ 227 public void addExtension( 228 String oid, 229 boolean critical, 230 byte[] value) 231 { 232 this.addExtension(new DERObjectIdentifier(oid), critical, value); 233 } 234 235 /** 236 * add a given extension field for the standard extensions tag (tag 3) 237 */ 238 public void addExtension( 239 DERObjectIdentifier oid, 240 boolean critical, 241 byte[] value) 242 { 243 if (extensions == null) 244 { 245 extensions = new Hashtable(); 246 extOrdering = new Vector(); 247 } 248 249 extensions.put(oid, new X509Extension(critical, new DEROctetString(value))); 250 extOrdering.addElement(oid); 251 } 252 253 /** 254 * add a given extension field for the standard extensions tag (tag 3) 255 * copying the extension value from another certificate. 256 * @throws CertificateParsingException if the extension cannot be extracted. 257 */ 258 public void copyAndAddExtension( 259 String oid, 260 boolean critical, 261 X509Certificate cert) 262 throws CertificateParsingException 263 { 264 byte[] extValue = cert.getExtensionValue(oid); 265 266 if (extValue == null) 267 { 268 throw new CertificateParsingException("extension " + oid + " not present"); 269 } 270 271 try 272 { 273 ASN1Encodable value = X509ExtensionUtil.fromExtensionValue(extValue); 274 275 this.addExtension(oid, critical, value); 276 } 277 catch (IOException e) 278 { 279 throw new CertificateParsingException(e.toString()); 280 } 281 } 282 283 /** 284 * add a given extension field for the standard extensions tag (tag 3) 285 * copying the extension value from another certificate. 286 * @throws CertificateParsingException if the extension cannot be extracted. 287 */ 288 public void copyAndAddExtension( 289 DERObjectIdentifier oid, 290 boolean critical, 291 X509Certificate cert) 292 throws CertificateParsingException 293 { 294 this.copyAndAddExtension(oid.getId(), critical, cert); 295 } 296 297 /** 298 * generate an X509 certificate, based on the current issuer and subject 299 * using the default provider "BC". 300 */ 301 public X509Certificate generateX509Certificate( 302 PrivateKey key) 303 throws SecurityException, SignatureException, InvalidKeyException 304 { 305 try 306 { 307 return generateX509Certificate(key, "BC", null); 308 } 309 catch (NoSuchProviderException e) 310 { 311 throw new SecurityException("BC provider not installed!"); 312 } 313 } 314 315 /** 316 * generate an X509 certificate, based on the current issuer and subject 317 * using the default provider "BC", and the passed in source of randomness 318 * (if required). 319 */ 320 public X509Certificate generateX509Certificate( 321 PrivateKey key, 322 SecureRandom random) 323 throws SecurityException, SignatureException, InvalidKeyException 324 { 325 try 326 { 327 return generateX509Certificate(key, "BC", random); 328 } 329 catch (NoSuchProviderException e) 330 { 331 throw new SecurityException("BC provider not installed!"); 332 } 333 } 334 335 /** 336 * generate an X509 certificate, based on the current issuer and subject, 337 * using the passed in provider for the signing. 338 */ 339 public X509Certificate generateX509Certificate( 340 PrivateKey key, 341 String provider) 342 throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException 343 { 344 return generateX509Certificate(key, provider, null); 345 } 346 347 /** 348 * generate an X509 certificate, based on the current issuer and subject, 349 * using the passed in provider for the signing and the supplied source 350 * of randomness, if required. 351 */ 352 public X509Certificate generateX509Certificate( 353 PrivateKey key, 354 String provider, 355 SecureRandom random) 356 throws NoSuchProviderException, SecurityException, SignatureException, InvalidKeyException 357 { 358 Signature sig = null; 359 360 if (sigOID == null) 361 { 362 throw new IllegalStateException("no signature algorithm specified"); 363 } 364 365 try 366 { 367 sig = Signature.getInstance(sigOID.getId(), provider); 368 } 369 catch (NoSuchAlgorithmException ex) 370 { 371 try 372 { 373 sig = Signature.getInstance(signatureAlgorithm, provider); 374 } 375 catch (NoSuchAlgorithmException e) 376 { 377 throw new SecurityException("exception creating signature: " + e.toString()); 378 } 379 } 380 381 if (random != null) 382 { 383 sig.initSign(key, random); 384 } 385 else 386 { 387 sig.initSign(key); 388 } 389 390 if (extensions != null) 391 { 392 tbsGen.setExtensions(new X509Extensions(extOrdering, extensions)); 393 } 394 395 TBSCertificateStructure tbsCert = tbsGen.generateTBSCertificate(); 396 397 try 398 { 399 ByteArrayOutputStream bOut = new ByteArrayOutputStream(); 400 DEROutputStream dOut = new DEROutputStream(bOut); 401 402 dOut.writeObject(tbsCert); 403 404 sig.update(bOut.toByteArray()); 405 } 406 catch (Exception e) 407 { 408 throw new SecurityException("exception encoding TBS cert - " + e); 409 } 410 411 ASN1EncodableVector v = new ASN1EncodableVector(); 412 413 v.add(tbsCert); 414 v.add(sigAlgId); 415 v.add(new DERBitString(sig.sign())); 416 417 return new X509CertificateObject(new X509CertificateStructure(new DERSequence(v))); 418 } 419 420 /** 421 * Return an iterator of the signature names supported by the generator. 422 * 423 * @return an iterator containing recognised names. 424 */ 425 public Iterator getSignatureAlgNames() 426 { 427 return X509Util.getAlgNames(); 428 } 429} 430