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() );
}
}
}