/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.security.cert; import java.io.ByteArrayInputStream; import java.io.NotSerializableException; import java.io.ObjectStreamException; import java.io.ObjectStreamField; import java.io.Serializable; import java.util.Iterator; import java.util.List; /** * An immutable certificate path that can be validated. All certificates in the * path are of the same type (i.e., X509). * <p> * A {@code CertPath} can be represented as a byte array in at least one * supported encoding scheme (i.e. PkiPath or PKCS7) when serialized. * <p> * When a {@code List} of the certificates is obtained it must be immutable. * <p> * A {@code CertPath} must be thread-safe without requiring coordinated access. * * @see Certificate */ public abstract class CertPath implements Serializable { private static final long serialVersionUID = 6068470306649138683L; // Standard name of the type of certificates in this path private final String type; /** * Creates a new {@code CertPath} instance for the specified certificate * type. * * @param type * the certificate type. */ protected CertPath(String type) { this.type = type; } /** * Returns the type of {@code Certificate} in this instance. * * @return the certificate type. */ public String getType() { return type; } /** * Returns {@code true} if {@code Certificate}s in the list are the same * type and the lists are equal (and by implication the certificates * contained within are the same). * * @param other * {@code CertPath} to be compared for equality. * @return {@code true} if the object are equal, {@code false} otherwise. */ public boolean equals(Object other) { if (this == other) { return true; } if (other instanceof CertPath) { CertPath o = (CertPath)other; if (getType().equals(o.getType())) { if (getCertificates().equals(o.getCertificates())) { return true; } } } return false; } /** * Overrides {@code Object.hashCode()}. The function is defined as follows: * <pre> * {@code hashCode = 31 * path.getType().hashCode() + * path.getCertificates().hashCode();} * </pre> * * @return the hash code for this instance. */ public int hashCode() { int hash = getType().hashCode(); hash = hash*31 + getCertificates().hashCode(); return hash; } /** * Returns a {@code String} representation of this {@code CertPath} * instance. It is the result of calling {@code toString} on all {@code * Certificate}s in the {@code List}. * * @return a string representation of this instance. */ public String toString() { StringBuilder sb = new StringBuilder(getType()); sb.append(" Cert Path, len="); sb.append(getCertificates().size()); sb.append(": [\n"); int n=1; for (Iterator<? extends Certificate> i=getCertificates().iterator(); i.hasNext(); n++) { sb.append("---------------certificate "); sb.append(n); sb.append("---------------\n"); sb.append(((Certificate)i.next()).toString()); } sb.append("\n]"); return sb.toString(); } /** * Returns an immutable List of the {@code Certificate}s contained * in the {@code CertPath}. * * @return a list of {@code Certificate}s in the {@code CertPath}. */ public abstract List<? extends Certificate> getCertificates(); /** * Returns an encoding of the {@code CertPath} using the default encoding. * * @return default encoding of the {@code CertPath}. * @throws CertificateEncodingException * if the encoding fails. */ public abstract byte[] getEncoded() throws CertificateEncodingException; /** * Returns an encoding of this {@code CertPath} using the given * {@code encoding} from {@link #getEncodings()}. * * @throws CertificateEncodingException * if the encoding fails. */ public abstract byte[] getEncoded(String encoding) throws CertificateEncodingException; /** * Returns an {@code Iterator} over the supported encodings for a * representation of the certificate path. * * @return {@code Iterator} over supported encodings (as {@code String}s). */ public abstract Iterator<String> getEncodings(); /** * Returns an alternate object to be serialized. * * @return an alternate object to be serialized. * @throws ObjectStreamException * if the creation of the alternate object fails. */ protected Object writeReplace() throws ObjectStreamException { try { return new CertPathRep(getType(), getEncoded()); } catch (CertificateEncodingException e) { throw new NotSerializableException("Could not create serialization object: " + e); } } /** * The alternate {@code Serializable} class to be used for serialization and * deserialization on {@code CertPath} objects. */ protected static class CertPathRep implements Serializable { private static final long serialVersionUID = 3015633072427920915L; // Standard name of the type of certificates in this path private final String type; // cert path data private final byte[] data; // Force default serialization to use writeUnshared/readUnshared // for cert path data private static final ObjectStreamField[] serialPersistentFields = { new ObjectStreamField("type", String.class), new ObjectStreamField("data", byte[].class, true), }; /** * Creates a new {@code CertPathRep} instance with the specified type * and encoded data. * * @param type * the certificate type. * @param data * the encoded data. */ protected CertPathRep(String type, byte[] data) { this.type = type; this.data = data; } /** * Deserializes a {@code CertPath} from a serialized {@code CertPathRep} * object. * * @return the deserialized {@code CertPath}. * @throws ObjectStreamException * if deserialization fails. */ protected Object readResolve() throws ObjectStreamException { try { CertificateFactory cf = CertificateFactory.getInstance(type); return cf.generateCertPath(new ByteArrayInputStream(data)); } catch (Throwable t) { throw new NotSerializableException("Could not resolve cert path: " + t); } } } }