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