1package org.bouncycastle.jcajce.provider.asymmetric.x509; 2 3import java.io.BufferedInputStream; 4import java.io.ByteArrayInputStream; 5import java.io.IOException; 6import java.io.InputStream; 7import java.io.PushbackInputStream; 8import java.security.cert.CRL; 9import java.security.cert.CRLException; 10import java.security.cert.CertPath; 11import java.security.cert.CertificateException; 12import java.security.cert.CertificateFactorySpi; 13import java.security.cert.CertificateParsingException; 14import java.security.cert.X509Certificate; 15import java.util.ArrayList; 16import java.util.Collection; 17import java.util.Iterator; 18import java.util.List; 19 20import org.bouncycastle.asn1.ASN1InputStream; 21import org.bouncycastle.asn1.ASN1ObjectIdentifier; 22import org.bouncycastle.asn1.ASN1Sequence; 23import org.bouncycastle.asn1.ASN1Set; 24import org.bouncycastle.asn1.ASN1TaggedObject; 25import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; 26import org.bouncycastle.asn1.pkcs.SignedData; 27import org.bouncycastle.asn1.x509.Certificate; 28import org.bouncycastle.asn1.x509.CertificateList; 29import org.bouncycastle.jcajce.util.BCJcaJceHelper; 30import org.bouncycastle.jcajce.util.JcaJceHelper; 31import org.bouncycastle.util.io.Streams; 32 33/** 34 * class for dealing with X509 certificates. 35 * <p> 36 * At the moment this will deal with "-----BEGIN CERTIFICATE-----" to "-----END CERTIFICATE-----" 37 * base 64 encoded certs, as well as the BER binaries of certificates and some classes of PKCS#7 38 * objects. 39 */ 40public class CertificateFactory 41 extends CertificateFactorySpi 42{ 43 private final JcaJceHelper bcHelper = new BCJcaJceHelper(); 44 45 private static final PEMUtil PEM_CERT_PARSER = new PEMUtil("CERTIFICATE"); 46 private static final PEMUtil PEM_CRL_PARSER = new PEMUtil("CRL"); 47 48 private ASN1Set sData = null; 49 private int sDataObjectCount = 0; 50 private InputStream currentStream = null; 51 52 private ASN1Set sCrlData = null; 53 private int sCrlDataObjectCount = 0; 54 private InputStream currentCrlStream = null; 55 56 private java.security.cert.Certificate readDERCertificate( 57 ASN1InputStream dIn) 58 throws IOException, CertificateParsingException 59 { 60 ASN1Sequence seq = (ASN1Sequence)dIn.readObject(); 61 62 if (seq.size() > 1 63 && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier) 64 { 65 if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) 66 { 67 sData = SignedData.getInstance(ASN1Sequence.getInstance( 68 (ASN1TaggedObject)seq.getObjectAt(1), true)).getCertificates(); 69 70 return getCertificate(); 71 } 72 } 73 74 return new X509CertificateObject(bcHelper, 75 Certificate.getInstance(seq)); 76 } 77 78 private java.security.cert.Certificate getCertificate() 79 throws CertificateParsingException 80 { 81 if (sData != null) 82 { 83 while (sDataObjectCount < sData.size()) 84 { 85 Object obj = sData.getObjectAt(sDataObjectCount++); 86 87 if (obj instanceof ASN1Sequence) 88 { 89 return new X509CertificateObject(bcHelper, 90 Certificate.getInstance(obj)); 91 } 92 } 93 } 94 95 return null; 96 } 97 98 private java.security.cert.Certificate readPEMCertificate( 99 InputStream in) 100 throws IOException, CertificateParsingException 101 { 102 ASN1Sequence seq = PEM_CERT_PARSER.readPEMObject(in); 103 104 if (seq != null) 105 { 106 return new X509CertificateObject(bcHelper, 107 Certificate.getInstance(seq)); 108 } 109 110 return null; 111 } 112 113 protected CRL createCRL(CertificateList c) 114 throws CRLException 115 { 116 return new X509CRLObject(bcHelper, c); 117 } 118 119 private CRL readPEMCRL( 120 InputStream in) 121 throws IOException, CRLException 122 { 123 ASN1Sequence seq = PEM_CRL_PARSER.readPEMObject(in); 124 125 if (seq != null) 126 { 127 return createCRL( 128 CertificateList.getInstance(seq)); 129 } 130 131 return null; 132 } 133 134 private CRL readDERCRL( 135 ASN1InputStream aIn) 136 throws IOException, CRLException 137 { 138 ASN1Sequence seq = (ASN1Sequence)aIn.readObject(); 139 140 if (seq.size() > 1 141 && seq.getObjectAt(0) instanceof ASN1ObjectIdentifier) 142 { 143 if (seq.getObjectAt(0).equals(PKCSObjectIdentifiers.signedData)) 144 { 145 sCrlData = SignedData.getInstance(ASN1Sequence.getInstance( 146 (ASN1TaggedObject)seq.getObjectAt(1), true)).getCRLs(); 147 148 return getCRL(); 149 } 150 } 151 152 return createCRL( 153 CertificateList.getInstance(seq)); 154 } 155 156 private CRL getCRL() 157 throws CRLException 158 { 159 if (sCrlData == null || sCrlDataObjectCount >= sCrlData.size()) 160 { 161 return null; 162 } 163 164 return createCRL( 165 CertificateList.getInstance( 166 sCrlData.getObjectAt(sCrlDataObjectCount++))); 167 } 168 169 /** 170 * Generates a certificate object and initializes it with the data 171 * read from the input stream inStream. 172 */ 173 public java.security.cert.Certificate engineGenerateCertificate( 174 InputStream in) 175 throws CertificateException 176 { 177 if (currentStream == null) 178 { 179 currentStream = in; 180 sData = null; 181 sDataObjectCount = 0; 182 } 183 else if (currentStream != in) // reset if input stream has changed 184 { 185 currentStream = in; 186 sData = null; 187 sDataObjectCount = 0; 188 } 189 190 try 191 { 192 if (sData != null) 193 { 194 if (sDataObjectCount != sData.size()) 195 { 196 return getCertificate(); 197 } 198 else 199 { 200 sData = null; 201 sDataObjectCount = 0; 202 return null; 203 } 204 } 205 206 InputStream pis; 207 208 if (in.markSupported()) 209 { 210 pis = in; 211 } 212 else 213 { 214 // BEGIN android-changed 215 // Was: pis = new ByteArrayInputStream(Streams.readAll(in)); 216 // Reason: we want {@code in.available()} to return the number of available bytes if 217 // there is trailing data (otherwise it breaks 218 // libcore.java.security.cert.X509CertificateTest#test_Provider 219 // ). Which is not possible if we read the whole stream at this point. 220 pis = new PushbackInputStream(in); 221 // END android-changed 222 } 223 224 // BEGIN android-changed 225 // Was: pis.mark(1); 226 if (in.markSupported()) { 227 pis.mark(1); 228 } 229 // END android-changed 230 231 int tag = pis.read(); 232 233 if (tag == -1) 234 { 235 return null; 236 } 237 238 // BEGIN android-changdd 239 // Was: pis.reset 240 if (in.markSupported()) { 241 pis.reset(); 242 } 243 else 244 { 245 ((PushbackInputStream) pis).unread(tag); 246 } 247 // END android-changed 248 249 if (tag != 0x30) // assume ascii PEM encoded. 250 { 251 return readPEMCertificate(pis); 252 } 253 else 254 { 255 return readDERCertificate(new ASN1InputStream(pis)); 256 } 257 } 258 catch (Exception e) 259 { 260 throw new ExCertificateException(e); 261 } 262 } 263 264 /** 265 * Returns a (possibly empty) collection view of the certificates 266 * read from the given input stream inStream. 267 */ 268 public Collection engineGenerateCertificates( 269 InputStream inStream) 270 throws CertificateException 271 { 272 java.security.cert.Certificate cert; 273 // BEGIN android-removed 274 // BufferedInputStream in = new BufferedInputStream(inStream); 275 // Reason: we want {@code in.available()} to return the number of available bytes if 276 // there is trailing data (otherwise it breaks 277 // libcore.java.security.cert.X509CertificateTest#test_Provider 278 // ). Which is not possible if we read the whole stream at this point. 279 // END android-removed 280 List certs = new ArrayList(); 281 282 // BEGIN android-changed 283 // Was: while ((cert = engineGenerateCertificate(in)) != null) 284 while ((cert = engineGenerateCertificate(inStream)) != null) 285 // END android-changed 286 { 287 certs.add(cert); 288 } 289 290 return certs; 291 } 292 293 /** 294 * Generates a certificate revocation list (CRL) object and initializes 295 * it with the data read from the input stream inStream. 296 */ 297 public CRL engineGenerateCRL( 298 InputStream in) 299 throws CRLException 300 { 301 if (currentCrlStream == null) 302 { 303 currentCrlStream = in; 304 sCrlData = null; 305 sCrlDataObjectCount = 0; 306 } 307 else if (currentCrlStream != in) // reset if input stream has changed 308 { 309 currentCrlStream = in; 310 sCrlData = null; 311 sCrlDataObjectCount = 0; 312 } 313 314 try 315 { 316 if (sCrlData != null) 317 { 318 if (sCrlDataObjectCount != sCrlData.size()) 319 { 320 return getCRL(); 321 } 322 else 323 { 324 sCrlData = null; 325 sCrlDataObjectCount = 0; 326 return null; 327 } 328 } 329 330 InputStream pis; 331 332 if (in.markSupported()) 333 { 334 pis = in; 335 } 336 else 337 { 338 pis = new ByteArrayInputStream(Streams.readAll(in)); 339 } 340 341 pis.mark(1); 342 int tag = pis.read(); 343 344 if (tag == -1) 345 { 346 return null; 347 } 348 349 pis.reset(); 350 if (tag != 0x30) // assume ascii PEM encoded. 351 { 352 return readPEMCRL(pis); 353 } 354 else 355 { // lazy evaluate to help processing of large CRLs 356 return readDERCRL(new ASN1InputStream(pis, true)); 357 } 358 } 359 catch (CRLException e) 360 { 361 throw e; 362 } 363 catch (Exception e) 364 { 365 throw new CRLException(e.toString()); 366 } 367 } 368 369 /** 370 * Returns a (possibly empty) collection view of the CRLs read from 371 * the given input stream inStream. 372 * 373 * The inStream may contain a sequence of DER-encoded CRLs, or 374 * a PKCS#7 CRL set. This is a PKCS#7 SignedData object, with the 375 * only signficant field being crls. In particular the signature 376 * and the contents are ignored. 377 */ 378 public Collection engineGenerateCRLs( 379 InputStream inStream) 380 throws CRLException 381 { 382 CRL crl; 383 List crls = new ArrayList(); 384 BufferedInputStream in = new BufferedInputStream(inStream); 385 386 while ((crl = engineGenerateCRL(in)) != null) 387 { 388 crls.add(crl); 389 } 390 391 return crls; 392 } 393 394 public Iterator engineGetCertPathEncodings() 395 { 396 return PKIXCertPath.certPathEncodings.iterator(); 397 } 398 399 public CertPath engineGenerateCertPath( 400 InputStream inStream) 401 throws CertificateException 402 { 403 return engineGenerateCertPath(inStream, "PkiPath"); 404 } 405 406 public CertPath engineGenerateCertPath( 407 InputStream inStream, 408 String encoding) 409 throws CertificateException 410 { 411 return new PKIXCertPath(inStream, encoding); 412 } 413 414 public CertPath engineGenerateCertPath( 415 List certificates) 416 throws CertificateException 417 { 418 Iterator iter = certificates.iterator(); 419 Object obj; 420 while (iter.hasNext()) 421 { 422 obj = iter.next(); 423 if (obj != null) 424 { 425 if (!(obj instanceof X509Certificate)) 426 { 427 throw new CertificateException("list contains non X509Certificate object while creating CertPath\n" + obj.toString()); 428 } 429 } 430 } 431 return new PKIXCertPath(certificates); 432 } 433 434 private class ExCertificateException 435 extends CertificateException 436 { 437 private Throwable cause; 438 439 public ExCertificateException(Throwable cause) 440 { 441 this.cause = cause; 442 } 443 444 public ExCertificateException(String msg, Throwable cause) 445 { 446 super(msg); 447 448 this.cause = cause; 449 } 450 451 public Throwable getCause() 452 { 453 return cause; 454 } 455 } 456} 457