blob: e13412d3a00f9303a80f6abc6b1863bfe92553c2 [file] [log] [blame]
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.
* <br />
**/
public class PKIXCertPath
extends CertPath
{
static final List certPathEncodings;
static
{
List encodings = new ArrayList();
encodings.add("PkiPath");
encodings.add("PEM");
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);
}
}
}