| 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.jcajce.util.BCJcaJceHelper; |
| import org.bouncycastle.jcajce.util.JcaJceHelper; |
| 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. |
| * <br /> |
| **/ |
| public class PKIXCertPath |
| extends CertPath |
| { |
| private final JcaJceHelper helper = new BCJcaJceHelper(); |
| |
| 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 = helper.createCertificateFactory("X.509"); |
| 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= helper.createCertificateFactory("X.509"); |
| 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); |
| } |
| } |
| } |