package org.bouncycastle.jcajce.provider.asymmetric.x509; import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStreamWriter; import java.security.NoSuchProviderException; import java.security.cert.CertPath; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import javax.security.auth.x500.X500Principal; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Encoding; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Primitive; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERSequence; import org.bouncycastle.asn1.DERSet; import org.bouncycastle.asn1.pkcs.ContentInfo; import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; import org.bouncycastle.asn1.pkcs.SignedData; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.io.pem.PemObject; // BEGIN android-removed // import org.bouncycastle.util.io.pem.PemWriter; // END android-removed /** * CertPath implementation for X.509 certificates. *
**/ public class PKIXCertPath extends CertPath { static final List certPathEncodings; static { List encodings = new ArrayList(); encodings.add("PkiPath"); // BEGIN android-removed // encodings.add("PEM"); // END android-removed encodings.add("PKCS7"); certPathEncodings = Collections.unmodifiableList(encodings); } private List certificates; /** * @param certs */ private List sortCerts( List certs) { if (certs.size() < 2) { return certs; } X500Principal issuer = ((X509Certificate)certs.get(0)).getIssuerX500Principal(); boolean okay = true; for (int i = 1; i != certs.size(); i++) { X509Certificate cert = (X509Certificate)certs.get(i); if (issuer.equals(cert.getSubjectX500Principal())) { issuer = ((X509Certificate)certs.get(i)).getIssuerX500Principal(); } else { okay = false; break; } } if (okay) { return certs; } // find end-entity cert List retList = new ArrayList(certs.size()); List orig = new ArrayList(certs); for (int i = 0; i < certs.size(); i++) { X509Certificate cert = (X509Certificate)certs.get(i); boolean found = false; X500Principal subject = cert.getSubjectX500Principal(); for (int j = 0; j != certs.size(); j++) { X509Certificate c = (X509Certificate)certs.get(j); if (c.getIssuerX500Principal().equals(subject)) { found = true; break; } } if (!found) { retList.add(cert); certs.remove(i); } } // can only have one end entity cert - something's wrong, give up. if (retList.size() > 1) { return orig; } for (int i = 0; i != retList.size(); i++) { issuer = ((X509Certificate)retList.get(i)).getIssuerX500Principal(); for (int j = 0; j < certs.size(); j++) { X509Certificate c = (X509Certificate)certs.get(j); if (issuer.equals(c.getSubjectX500Principal())) { retList.add(c); certs.remove(j); break; } } } // make sure all certificates are accounted for. if (certs.size() > 0) { return orig; } return retList; } PKIXCertPath(List certificates) { super("X.509"); this.certificates = sortCerts(new ArrayList(certificates)); } /** * Creates a CertPath of the specified type. * This constructor is protected because most users should use * a CertificateFactory to create CertPaths. **/ PKIXCertPath( InputStream inStream, String encoding) throws CertificateException { super("X.509"); try { if (encoding.equalsIgnoreCase("PkiPath")) { ASN1InputStream derInStream = new ASN1InputStream(inStream); ASN1Primitive derObject = derInStream.readObject(); if (!(derObject instanceof ASN1Sequence)) { throw new CertificateException("input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath"); } Enumeration e = ((ASN1Sequence)derObject).getObjects(); certificates = new ArrayList(); CertificateFactory certFactory = CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); while (e.hasMoreElements()) { ASN1Encodable element = (ASN1Encodable)e.nextElement(); byte[] encoded = element.toASN1Primitive().getEncoded(ASN1Encoding.DER); certificates.add(0, certFactory.generateCertificate( new ByteArrayInputStream(encoded))); } } else if (encoding.equalsIgnoreCase("PKCS7") || encoding.equalsIgnoreCase("PEM")) { inStream = new BufferedInputStream(inStream); certificates = new ArrayList(); CertificateFactory certFactory= CertificateFactory.getInstance("X.509", BouncyCastleProvider.PROVIDER_NAME); Certificate cert; while ((cert = certFactory.generateCertificate(inStream)) != null) { certificates.add(cert); } } else { throw new CertificateException("unsupported encoding: " + encoding); } } catch (IOException ex) { throw new CertificateException("IOException throw while decoding CertPath:\n" + ex.toString()); } catch (NoSuchProviderException ex) { throw new CertificateException("BouncyCastle provider not found while trying to get a CertificateFactory:\n" + ex.toString()); } this.certificates = sortCerts(certificates); } /** * Returns an iteration of the encodings supported by this * certification path, with the default encoding * first. Attempts to modify the returned Iterator via its * remove method result in an UnsupportedOperationException. * * @return an Iterator over the names of the supported encodings (as Strings) **/ public Iterator getEncodings() { return certPathEncodings.iterator(); } /** * Returns the encoded form of this certification path, using * the default encoding. * * @return the encoded bytes * @exception java.security.cert.CertificateEncodingException if an encoding error occurs **/ public byte[] getEncoded() throws CertificateEncodingException { Iterator iter = getEncodings(); if (iter.hasNext()) { Object enc = iter.next(); if (enc instanceof String) { return getEncoded((String)enc); } } return null; } /** * Returns the encoded form of this certification path, using * the specified encoding. * * @param encoding the name of the encoding to use * @return the encoded bytes * @exception java.security.cert.CertificateEncodingException if an encoding error * occurs or the encoding requested is not supported * **/ public byte[] getEncoded(String encoding) throws CertificateEncodingException { if (encoding.equalsIgnoreCase("PkiPath")) { ASN1EncodableVector v = new ASN1EncodableVector(); ListIterator iter = certificates.listIterator(certificates.size()); while (iter.hasPrevious()) { v.add(toASN1Object((X509Certificate)iter.previous())); } return toDEREncoded(new DERSequence(v)); } else if (encoding.equalsIgnoreCase("PKCS7")) { ContentInfo encInfo = new ContentInfo(PKCSObjectIdentifiers.data, null); ASN1EncodableVector v = new ASN1EncodableVector(); for (int i = 0; i != certificates.size(); i++) { v.add(toASN1Object((X509Certificate)certificates.get(i))); } SignedData sd = new SignedData( new ASN1Integer(1), new DERSet(), encInfo, new DERSet(v), null, new DERSet()); return toDEREncoded(new ContentInfo( PKCSObjectIdentifiers.signedData, sd)); } // BEGIN android-removed // else if (encoding.equalsIgnoreCase("PEM")) // { // ByteArrayOutputStream bOut = new ByteArrayOutputStream(); // PemWriter pWrt = new PemWriter(new OutputStreamWriter(bOut)); // // try // { // for (int i = 0; i != certificates.size(); i++) // { // pWrt.writeObject(new PemObject("CERTIFICATE", ((X509Certificate)certificates.get(i)).getEncoded())); // } // // pWrt.close(); // } // catch (Exception e) // { // throw new CertificateEncodingException("can't encode certificate for PEM encoded path"); // } // // return bOut.toByteArray(); // } // END android-removed else { throw new CertificateEncodingException("unsupported encoding: " + encoding); } } /** * Returns the list of certificates in this certification * path. The List returned must be immutable and thread-safe. * * @return an immutable List of Certificates (may be empty, but not null) **/ public List getCertificates() { return Collections.unmodifiableList(new ArrayList(certificates)); } /** * Return a DERObject containing the encoded certificate. * * @param cert the X509Certificate object to be encoded * * @return the DERObject **/ private ASN1Primitive toASN1Object( X509Certificate cert) throws CertificateEncodingException { try { return new ASN1InputStream(cert.getEncoded()).readObject(); } catch (Exception e) { throw new CertificateEncodingException("Exception while encoding certificate: " + e.toString()); } } private byte[] toDEREncoded(ASN1Encodable obj) throws CertificateEncodingException { try { return obj.toASN1Primitive().getEncoded(ASN1Encoding.DER); } catch (IOException e) { throw new CertificateEncodingException("Exception thrown: " + e); } } }