1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.security.cert;
19
20import java.io.ByteArrayInputStream;
21import java.io.NotSerializableException;
22import java.io.ObjectStreamException;
23import java.io.ObjectStreamField;
24import java.io.Serializable;
25import java.util.Iterator;
26import java.util.List;
27
28import org.apache.harmony.security.internal.nls.Messages;
29
30/**
31 * An immutable certificate path that can be validated. All certificates in the
32 * path are of the same type (i.e., X509).
33 * <p>
34 * A {@code CertPath} can be represented as a byte array in at least one
35 * supported encoding scheme (i.e. PkiPath or PKCS7) when serialized.
36 * <p>
37 * When a {@code List} of the certificates is obtained it must be immutable.
38 * <p>
39 * A {@code CertPath} must be thread-safe without requiring coordinated access.
40 *
41 * @see Certificate
42 */
43public abstract class CertPath implements Serializable {
44
45    private static final long serialVersionUID = 6068470306649138683L;
46    // Standard name of the type of certificates in this path
47    private final String type;
48
49    /**
50     * Creates a new {@code CertPath} instance for the specified certificate
51     * type.
52     *
53     * @param type
54     *            the certificate type.
55     */
56    protected CertPath(String type) {
57        this.type = type;
58    }
59
60    /**
61     * Returns the type of {@code Certificate} in this instance.
62     *
63     * @return the certificate type.
64     */
65    public String getType() {
66        return type;
67    }
68
69    /**
70     * Returns {@code true} if {@code Certificate}s in the list are the same
71     * type and the lists are equal (and by implication the certificates
72     * contained within are the same).
73     *
74     * @param other
75     *            {@code CertPath} to be compared for equality.
76     * @return {@code true} if the object are equal, {@code false} otherwise.
77     */
78    public boolean equals(Object other) {
79        if (this == other) {
80            return true;
81        }
82        if (other instanceof CertPath) {
83            CertPath o = (CertPath)other;
84            if (getType().equals(o.getType())) {
85                if (getCertificates().equals(o.getCertificates())) {
86                    return true;
87                }
88            }
89        }
90        return false;
91    }
92
93    /**
94     * Overrides {@code Object.hashCode()}. The function is defined as follows:
95     * <pre>
96     * {@code hashCode = 31 * path.getType().hashCode() +
97     * path.getCertificates().hashCode();}
98     * </pre>
99     *
100     * @return the hash code for this instance.
101     */
102    public int hashCode() {
103        int hash = getType().hashCode();
104        hash = hash*31 + getCertificates().hashCode();
105        return hash;
106    }
107
108    /**
109     * Returns a {@code String} representation of this {@code CertPath}
110     * instance. It is the result of calling {@code toString} on all {@code
111     * Certificate}s in the {@code List}.
112     *
113     * @return a string representation of this instance.
114     */
115    public String toString() {
116        StringBuilder sb = new StringBuilder(getType());
117        sb.append(" Cert Path, len="); //$NON-NLS-1$
118        sb.append(getCertificates().size());
119        sb.append(": [\n"); //$NON-NLS-1$
120        int n=1;
121        // BEGIN android-changed
122        for (Iterator<? extends Certificate> i=getCertificates().iterator();
123                      i.hasNext(); n++) {
124            sb.append("---------------certificate "); //$NON-NLS-1$
125            sb.append(n);
126            sb.append("---------------\n"); //$NON-NLS-1$
127            sb.append(((Certificate)i.next()).toString());
128        }
129        // END android-changed
130        sb.append("\n]"); //$NON-NLS-1$
131        return sb.toString();
132    }
133
134    /**
135     * Returns an immutable List of the {@code Certificate}s contained
136     * in the {@code CertPath}.
137     *
138     * @return a list of {@code Certificate}s in the {@code CertPath}.
139     */
140    public abstract List<? extends Certificate> getCertificates();
141
142    /**
143     * Returns an encoding of the {@code CertPath} using the default encoding.
144     *
145     * @return default encoding of the {@code CertPath}.
146     * @throws CertificateEncodingException
147     *             if the encoding fails.
148     */
149    public abstract byte[] getEncoded()
150        throws CertificateEncodingException;
151
152    /**
153     * Returns an encoding of the {@code CertPath} using the specified encoding.
154     *
155     * @param encoding
156     *            encoding that should be generated.
157     * @return default encoding of the {@code CertPath}.
158     * @throws CertificateEncodingException
159     *             if the encoding fails.
160     */
161    public abstract byte[] getEncoded(String encoding)
162        throws CertificateEncodingException;
163
164    /**
165     * Returns an {@code Iterator} over the supported encodings for a
166     * representation of the certificate path.
167     *
168     * @return {@code Iterator} over supported encodings (as {@code String}s).
169     */
170    public abstract Iterator<String> getEncodings();
171
172    /**
173     * Returns an alternate object to be serialized.
174     *
175     * @return an alternate object to be serialized.
176     * @throws ObjectStreamException
177     *             if the creation of the alternate object fails.
178     */
179    protected Object writeReplace() throws ObjectStreamException {
180        try {
181            return new CertPathRep(getType(), getEncoded());
182        } catch (CertificateEncodingException e) {
183            throw new NotSerializableException (
184                    Messages.getString("security.66", e)); //$NON-NLS-1$
185        }
186    }
187
188    /**
189     * The alternate {@code Serializable} class to be used for serialization and
190     * deserialization on {@code CertPath} objects.
191     */
192    protected static class CertPathRep implements Serializable {
193
194        private static final long serialVersionUID = 3015633072427920915L;
195        // Standard name of the type of certificates in this path
196        private final String type;
197        // cert path data
198        private final byte[] data;
199
200        // Force default serialization to use writeUnshared/readUnshared
201        // for cert path data
202        private static final ObjectStreamField[] serialPersistentFields = {
203             new ObjectStreamField("type", String.class), //$NON-NLS-1$
204             new ObjectStreamField("data", byte[].class, true) //$NON-NLS-1$
205        };
206
207        /**
208         * Creates a new {@code CertPathRep} instance with the specified type
209         * and encoded data.
210         *
211         * @param type
212         *            the certificate type.
213         * @param data
214         *            the encoded data.
215         */
216        protected CertPathRep(String type, byte[] data) {
217            this.type = type;
218            this.data = data;
219        }
220
221        /**
222         * Deserializes a {@code CertPath} from a serialized {@code CertPathRep}
223         * object.
224         *
225         * @return the deserialized {@code CertPath}.
226         * @throws ObjectStreamException
227         *             if deserialization fails.
228         */
229        protected Object readResolve() throws ObjectStreamException {
230            try {
231                CertificateFactory cf = CertificateFactory.getInstance(type);
232                return cf.generateCertPath(new ByteArrayInputStream(data));
233            } catch (Throwable t) {
234                throw new NotSerializableException(
235                        Messages.getString("security.67", t)); //$NON-NLS-1$
236            }
237        }
238    }
239}
240