package org.bouncycastle.jce.provider; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.security.NoSuchProviderException; import java.security.cert.CertPath; 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 org.bouncycastle.asn1.ASN1EncodableVector; import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.DERInputStream; import org.bouncycastle.asn1.DERObject; import org.bouncycastle.asn1.DEROutputStream; import org.bouncycastle.asn1.DERSequence; /** * CertPath implementation for X.509 certificates. * <br /> * <b>TODO: add PKCS #7 encoding support</b> **/ public class PKIXCertPath extends CertPath { static final List certPathEncodings; static { List encodings = new ArrayList(); encodings.add("PkiPath"); certPathEncodings = Collections.unmodifiableList( encodings ); } private List certificates; /** * Creates a CertPath of the specified type. * This constructor is protected because most users should use * a CertificateFactory to create CertPaths. * @param type the standard name of the type of Certificatesin this path **/ PKIXCertPath( List certificates ) { super("X.509"); this.certificates = new ArrayList( certificates ); } /** * Creates a CertPath of the specified type. * This constructor is protected because most users should use * a CertificateFactory to create CertPaths. * * <b>TODO: implement PKCS7 decoding</b> * * @param type the standard name of the type of Certificatesin this path **/ PKIXCertPath( InputStream inStream, String encoding) throws CertificateException { super("X.509"); try { if ( encoding.equals( "PkiPath" ) ) { DERInputStream derInStream = new DERInputStream(inStream); DERObject derObject = derInStream.readObject(); if ( derObject == null || ! ( derObject instanceof ASN1Sequence ) ) { throw new CertificateException("input stream does not contain a ASN1 SEQUENCE while reading PkiPath encoded data to load CertPath" ); } Enumeration enumx = ((ASN1Sequence)derObject).getObjects(); InputStream certInStream; ByteArrayOutputStream outStream; DEROutputStream derOutStream; certificates = new ArrayList(); CertificateFactory certFactory= CertificateFactory.getInstance( "X.509", "BC" ); while ( enumx.hasMoreElements() ) { outStream = new ByteArrayOutputStream(); derOutStream = new DEROutputStream(outStream); derOutStream.writeObject(enumx.nextElement()); derOutStream.close(); certInStream = new ByteArrayInputStream(outStream.toByteArray()); certificates.add(0,certFactory.generateCertificate(certInStream)); } } else { throw new CertificateException( "unsupported 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() ); } } /** * 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 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. * * <b>TODO: implement PKCS7 decoding</b> * * @param encoding the name of the encoding to use * @return the encoded bytes * @exception CertificateEncodingException if an encoding error * occurs or the encoding requested is not supported * **/ public byte[] getEncoded(String encoding) throws CertificateEncodingException { DERObject encoded = null; if ( encoding.equals("PkiPath") ) { ASN1EncodableVector v = new ASN1EncodableVector(); // TODO check ListIterator implementation for JDK 1.1 ListIterator iter = certificates.listIterator(certificates.size()); while ( iter.hasPrevious() ) { v.add(getEncodedX509Certificate((X509Certificate)iter.previous())); } encoded = new DERSequence(v); } else throw new CertificateEncodingException( "unsupported encoding" ); if ( encoded == null ) return null; ByteArrayOutputStream outStream = new ByteArrayOutputStream(); DEROutputStream derOutStream = new DEROutputStream(outStream); try { derOutStream.writeObject( encoded ); derOutStream.close(); } catch ( IOException ex ) { throw new CertificateEncodingException( "IOExeption thrown: " + ex.toString() ); } return outStream.toByteArray(); } /** * Returns the list of certificates in this certification * path. The List returned must be immutable and thread-safe. * * <b>TODO: return immutable List</b> * * @return an immutable List of Certificates (may be empty, but not null) **/ public List getCertificates() { return new ArrayList( certificates ); } /** * Return a DERObject containing the encoded certificate. * * @param cert the X509Certificate object to be encoded * * @return the DERObject **/ private DERObject getEncodedX509Certificate( X509Certificate cert ) throws CertificateEncodingException { try { ByteArrayInputStream inStream = new ByteArrayInputStream( cert.getEncoded() ); DERInputStream derInStream = new DERInputStream( inStream ); return derInStream.readObject(); } catch ( IOException ex ) { throw new CertificateEncodingException( "IOException caught while encoding certificate\n" + ex.toString() ); } } }