package org.bouncycastle.jce.cert;
import java.io.ByteArrayInputStream;
import java.io.NotSerializableException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* An immutable sequence of certificates (a certification path).<br />
* <br />
* This is an abstract class that defines the methods common to all CertPaths.
* Subclasses can handle different kinds of certificates (X.509, PGP, etc.).<br />
* <br />
* All CertPath objects have a type, a list of Certificates, and one or more
* supported encodings. Because the CertPath class is immutable, a CertPath
* cannot change in any externally visible way after being constructed. This
* stipulation applies to all public fields and methods of this class and any
* added or overridden by subclasses.<br />
* <br />
* The type is a String that identifies the type of Certificates in the
* certification path. For each certificate cert in a certification path
* certPath, cert.getType().equals(certPath.getType()) must be true.<br />
* <br />
* The list of Certificates is an ordered List of zero or more Certificates.
* This List and all of the Certificates contained in it must be immutable.<br />
* <br />
* Each CertPath object must support one or more encodings so that the object
* can be translated into a byte array for storage or transmission to other
* parties. Preferably, these encodings should be well-documented standards
* (such as PKCS#7). One of the encodings supported by a CertPath is considered
* the default encoding. This encoding is used if no encoding is explicitly
* requested (for the {@link #getEncoded()} method, for instance).<br />
* <br />
* All CertPath objects are also Serializable. CertPath objects are resolved
* into an alternate {@link CertPathRep} object during serialization. This
* allows a CertPath object to be serialized into an equivalent representation
* regardless of its underlying implementation.<br />
* <br />
* CertPath objects can be created with a CertificateFactory or they can be
* returned by other classes, such as a CertPathBuilder.<br />
* <br />
* By convention, X.509 CertPaths (consisting of X509Certificates), are ordered
* starting with the target certificate and ending with a certificate issued by
* the trust anchor. That is, the issuer of one certificate is the subject of
* the following one. The certificate representing the
* {@link TrustAnchor TrustAnchor} should not be included in the certification
* path. Unvalidated X.509 CertPaths may not follow these conventions. PKIX
* CertPathValidators will detect any departure from these conventions that
* cause the certification path to be invalid and throw a
* CertPathValidatorException.<br />
* <br />
* <strong>Concurrent Access</strong><br />
* <br />
* All CertPath objects must be thread-safe. That is, multiple threads may
* concurrently invoke the methods defined in this class on a single CertPath
* object (or more than one) with no ill effects. This is also true for the List
* returned by CertPath.getCertificates.<br />
* <br />
* Requiring CertPath objects to be immutable and thread-safe allows them to be
* passed around to various pieces of code without worrying about coordinating
* access. Providing this thread-safety is generally not difficult, since the
* CertPath and List objects in question are immutable.
*
* @see CertificateFactory
* @see CertPathBuilder
*/
public abstract class CertPath extends Object implements Serializable
{
private String type;
/**
* Alternate <code>CertPath</code> class for serialization.
*/
protected static class CertPathRep implements Serializable
{
private String type;
private byte[] data;
/**
* Creates a <code>CertPathRep</code> with the specified type and
* encoded form of a certification path.
*
* @param type
* the standard name of a CertPath
* @param typedata
* the encoded form of the certification path
*/
protected CertPathRep(String type, byte[] data)
{
this.type = type;
this.data = data;
}
/**
* Returns a CertPath constructed from the type and data.
*
* @return the resolved CertPath object
* @exception ObjectStreamException
* if a CertPath could not be constructed
*/
protected Object readResolve() throws ObjectStreamException
{
try
{
ByteArrayInputStream inStream = new ByteArrayInputStream(data);
CertificateFactory cf = CertificateFactory.getInstance(type);
return cf.generateCertPath(inStream);
}
catch (CertificateException ce)
{
throw new NotSerializableException(
" java.security.cert.CertPath: " + type);
}
}
}
/**
* 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
*/
protected CertPath(String type)
{
this.type = type;
}
/**
* Returns the type of Certificates in this certification path. This is the
* same string that would be returned by
* {@link java.security.cert.Certificate#getType()} for all Certificates in
* the certification path.
*
* @return the type of Certificates in this certification path (never null)
*/
public String getType()
{
return type;
}
/**
* 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 abstract Iterator getEncodings();
/**
* Compares this certification path for equality with the specified object.
* Two CertPaths are equal if and only if their types are equal and their
* certificate Lists (and by implication the Certificates in those Lists)
* are equal. A CertPath is never equal to an object that is not a CertPath.<br />
* <br />
* This algorithm is implemented by this method. If it is overridden, the
* behavior specified here must be maintained.
*
* @param other
* the object to test for equality with this certification path
*
* @return true if the specified object is equal to this certification path,
* false otherwise
*
* @see Object#hashCode() Object.hashCode()
*/
public boolean equals(Object other)
{
if (!(other instanceof CertPath))
{
return false;
}
CertPath otherCertPath = (CertPath)other;
if (!getType().equals(otherCertPath.getType()))
{
return false;
}
return getCertificates().equals(otherCertPath.getCertificates());
}
/**
* Returns the hashcode for this certification path. The hash code of a
* certification path is defined to be the result of the following
* calculation:
*
* <pre>
* hashCode = path.getType().hashCode();
* hashCode = 31 * hashCode + path.getCertificates().hashCode();
* </pre>
*
* This ensures that path1.equals(path2) implies that
* path1.hashCode()==path2.hashCode() for any two certification paths, path1
* and path2, as required by the general contract of Object.hashCode.
*
* @return The hashcode value for this certification path
*
* @see #equals(Object)
*/
public int hashCode()
{
return getType().hashCode() * 31 + getCertificates().hashCode();
}
/**
* Returns a string representation of this certification path. This calls
* the toString method on each of the Certificates in the path.
*
* @return a string representation of this certification path
*/
public String toString()
{
StringBuffer s = new StringBuffer();
List certs = getCertificates();
ListIterator iter = certs.listIterator();
s.append('\n').append(getType()).append(" Cert Path: length = ").append(certs.size())
.append("\n[\n");
while (iter.hasNext())
{
s
.append("=========================================================Certificate ")
.append(iter.nextIndex()).append('\n');
s.append(iter.next()).append('\n');
s
.append("========================================================Certificate end\n\n\n");
}
s.append("\n]");
return s.toString();
}
/**
* Returns the encoded form of this certification path, using the default
* encoding.
*
* @return the encoded bytes
*
* @exception CertificateEncodingException
* if an encoding error occurs
*/
public abstract byte[] getEncoded() throws CertificateEncodingException;
/**
* 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 CertificateEncodingException
* if an encoding error occurs or the encoding requested is
* not supported
*/
public abstract byte[] getEncoded(String encoding)
throws CertificateEncodingException;
/**
* 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 abstract List getCertificates();
/**
* Replaces the CertPath to be serialized with a CertPathRep object.
*
* @return the CertPathRep to be serialized
*
* @exception ObjectStreamException
* if a CertPathRep object representing this certification
* path could not be created
*/
protected Object writeReplace() throws ObjectStreamException
{
try
{
return new CertPathRep(getType(), getEncoded());
}
catch (CertificateException ce)
{
throw new NotSerializableException(" java.security.cert.CertPath: "
+ getType());
}
}
}