package gnu.java.security.x509;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.CertPath;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import gnu.java.security.OID;
import gnu.java.security.der.DER;
import gnu.java.security.der.DEREncodingException;
import gnu.java.security.der.DERReader;
import gnu.java.security.der.DERValue;
public class X509CertPath extends CertPath
{
public static final List ENCODINGS = Collections.unmodifiableList(
Arrays.asList(new String[] { "PkiPath", "PKCS7" }));
private static final OID PKCS7_SIGNED_DATA = new OID("1.2.840.113549.1.7.2");
private static final OID PKCS7_DATA = new OID("1.2.840.113549.1.7.1");
private List path;
private byte[] pkcs_encoded;
private byte[] pki_encoded;
public X509CertPath(List path)
{
super("X.509");
this.path = Collections.unmodifiableList(path);
}
public X509CertPath(InputStream in) throws CertificateEncodingException
{
this(in, (String) ENCODINGS.get(0));
}
public X509CertPath(InputStream in, String encoding)
throws CertificateEncodingException
{
super("X.509");
try
{
parse(in, encoding);
}
catch (IOException ioe)
{
throw new CertificateEncodingException();
}
}
public List getCertificates()
{
return path; }
public byte[] getEncoded() throws CertificateEncodingException
{
return getEncoded((String) ENCODINGS.get(0));
}
public byte[] getEncoded(String encoding) throws CertificateEncodingException
{
if (encoding.equalsIgnoreCase("PkiPath"))
{
if (pki_encoded == null)
{
try
{
pki_encoded = encodePki();
}
catch (IOException ioe)
{
throw new CertificateEncodingException();
}
}
return (byte[]) pki_encoded.clone();
}
else if (encoding.equalsIgnoreCase("PKCS7"))
{
if (pkcs_encoded == null)
{
try
{
pkcs_encoded = encodePKCS();
}
catch (IOException ioe)
{
throw new CertificateEncodingException();
}
}
return (byte[]) pkcs_encoded.clone();
}
else
throw new CertificateEncodingException("unknown encoding: " + encoding);
}
public Iterator getEncodings()
{
return ENCODINGS.iterator(); }
private void parse(InputStream in, String encoding)
throws CertificateEncodingException, IOException
{
DERReader der = new DERReader(in);
DERValue path = null;
if (encoding.equalsIgnoreCase("PkiPath"))
{
path = der.read();
if (!path.isConstructed())
throw new DEREncodingException("malformed PkiPath");
}
else if (encoding.equalsIgnoreCase("PKCS7"))
{
DERValue value = der.read();
if (!value.isConstructed())
throw new DEREncodingException("malformed ContentInfo");
value = der.read();
if (!(value.getValue() instanceof OID) ||
((OID) value.getValue()).equals(PKCS7_SIGNED_DATA))
throw new DEREncodingException("not a SignedData");
value = der.read();
if (!value.isConstructed() || value.getTag() != 0)
throw new DEREncodingException("malformed content");
value = der.read();
if (value.getTag() != DER.INTEGER)
throw new DEREncodingException("malformed Version");
value = der.read();
if (!value.isConstructed() || value.getTag() != DER.SET)
throw new DEREncodingException("malformed DigestAlgorithmIdentifiers");
der.skip(value.getLength());
value = der.read();
if (!value.isConstructed())
throw new DEREncodingException("malformed ContentInfo");
der.skip(value.getLength());
path = der.read();
if (!path.isConstructed() || path.getTag() != 0)
throw new DEREncodingException("no certificates");
}
else
throw new CertificateEncodingException("unknown encoding: " + encoding);
LinkedList certs = new LinkedList();
int len = 0;
while (len < path.getLength())
{
DERValue cert = der.read();
try
{
certs.add(new X509Certificate(new ByteArrayInputStream(cert.getEncoded())));
}
catch (CertificateException ce)
{
throw new CertificateEncodingException(ce.getMessage());
}
len += cert.getEncodedLength();
der.skip(cert.getLength());
}
this.path = Collections.unmodifiableList(certs);
}
private byte[] encodePki()
throws CertificateEncodingException, IOException
{
synchronized (path)
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (Iterator i = path.iterator(); i.hasNext(); )
{
out.write(((Certificate) i.next()).getEncoded());
}
byte[] b = out.toByteArray();
DERValue val = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
b.length, b, null);
return val.getEncoded();
}
}
private byte[] encodePKCS()
throws CertificateEncodingException, IOException
{
synchronized (path)
{
ArrayList signedData = new ArrayList(5);
signedData.add(new DERValue(DER.INTEGER, BigInteger.ONE));
signedData.add(new DERValue(DER.CONSTRUCTED | DER.SET,
Collections.EMPTY_SET));
signedData.add(new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
Collections.singletonList(
new DERValue(DER.OBJECT_IDENTIFIER, PKCS7_DATA))));
ByteArrayOutputStream out = new ByteArrayOutputStream();
for (Iterator i = path.iterator(); i.hasNext(); )
{
out.write(((Certificate) i.next()).getEncoded());
}
byte[] b = out.toByteArray();
signedData.add(new DERValue(DER.CONSTRUCTED | DER.CONTEXT,
b.length, b, null));
DERValue sdValue = new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
signedData);
ArrayList contentInfo = new ArrayList(2);
contentInfo.add(new DERValue(DER.OBJECT_IDENTIFIER, PKCS7_SIGNED_DATA));
contentInfo.add(new DERValue(DER.CONSTRUCTED | DER.CONTEXT, sdValue));
return new DERValue(DER.CONSTRUCTED | DER.SEQUENCE,
contentInfo).getEncoded();
}
}
}