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.security.InvalidKeyException;
26import java.security.NoSuchAlgorithmException;
27import java.security.NoSuchProviderException;
28import java.security.PublicKey;
29import java.security.SignatureException;
30import java.util.Arrays;
31
32/**
33 * Abstract class to represent identity certificates. It represents a way to
34 * verify the binding of a Principal and its public key. Examples are X.509,
35 * PGP, and SDSI.
36 */
37public abstract class Certificate implements Serializable {
38
39    private static final long serialVersionUID = -3585440601605666277L;
40
41    // The standard name of the certificate type
42    private final String type;
43
44    /**
45     * Creates a new {@code Certificate} with the specified type.
46     *
47     * @param type
48     *        the certificate type.
49     */
50    protected Certificate(String type) {
51        this.type = type;
52    }
53
54    /**
55     * Returns the certificate type.
56     *
57     * @return the certificate type.
58     */
59    public final String getType() {
60        return type;
61    }
62
63    /**
64     * Compares the argument to the certificate, and returns {@code true} if they
65     * represent the <em>same</em> object using a class specific comparison. The
66     * implementation in Object returns {@code true} only if the argument is the
67     * exact same object as the callee (==).
68     *
69     * @param other
70     *            the object to compare with this object.
71     * @return {@code true} if the object is the same as this object, {@code
72     *         false} if it is different from this object.
73     * @see #hashCode
74     */
75    public boolean equals(Object other) {
76        // obj equal to itself
77        if (this == other) {
78            return true;
79        }
80        if (other instanceof Certificate) {
81            try {
82                // check that encoded forms match
83                return Arrays.equals(this.getEncoded(),
84                        ((Certificate)other).getEncoded());
85            } catch (CertificateEncodingException e) {
86                throw new RuntimeException(e);
87            }
88        }
89        return false;
90    }
91
92    /**
93     * Returns an integer hash code for the certificate. Any two objects which
94     * return {@code true} when passed to {@code equals} must return the same
95     * value for this method.
96     *
97     * @return the certificate's hash
98     * @see #equals
99     */
100    public int hashCode() {
101        try {
102            byte[] encoded = getEncoded();
103            int hash = 0;
104            for (int i=0; i<encoded.length; i++) {
105                hash += i*encoded[i];
106            }
107            return hash;
108        } catch (CertificateEncodingException e) {
109            throw new RuntimeException(e);
110        }
111    }
112
113    /**
114     * Returns the encoded representation for this certificate.
115     *
116     * @return the encoded representation for this certificate.
117     * @throws CertificateEncodingException
118     *             if the encoding fails.
119     */
120    public abstract byte[] getEncoded() throws CertificateEncodingException;
121
122    /**
123     * Verifies that this certificate was signed with the given public key.
124     *
125     * @param key
126     *            PublicKey public key for which verification should be
127     *            performed.
128     * @throws CertificateException
129     *             if encoding errors are detected.
130     * @throws NoSuchAlgorithmException
131     *             if an unsupported algorithm is detected.
132     * @throws InvalidKeyException
133     *             if an invalid key is detected.
134     * @throws NoSuchProviderException
135     *             if there is no default provider.
136     * @throws SignatureException
137     *             if signature errors are detected.
138     */
139    public abstract void verify(PublicKey key)
140        throws CertificateException,
141               NoSuchAlgorithmException,
142               InvalidKeyException,
143               NoSuchProviderException,
144               SignatureException;
145
146    /**
147     * Verifies that this certificate was signed with the given public key. It
148     * Uses the signature algorithm given by the provider.
149     *
150     * @param key
151     *            PublicKey public key for which verification should be
152     *            performed.
153     * @param sigProvider
154     *            String the name of the signature provider.
155     * @throws CertificateException
156     *                if encoding errors are detected.
157     * @throws NoSuchAlgorithmException
158     *                if an unsupported algorithm is detected.
159     * @throws InvalidKeyException
160     *                if an invalid key is detected.
161     * @throws NoSuchProviderException
162     *                if the specified provider does not exists.
163     * @throws SignatureException
164     *                if signature errors are detected.
165     */
166    public abstract void verify(PublicKey key, String sigProvider)
167        throws CertificateException,
168               NoSuchAlgorithmException,
169               InvalidKeyException,
170               NoSuchProviderException,
171               SignatureException;
172
173    /**
174     * Returns a string containing a concise, human-readable description of the
175     * certificate.
176     *
177     * @return a printable representation for the certificate.
178     */
179    public abstract String toString();
180
181    /**
182     * Returns the public key corresponding to this certificate.
183     *
184     * @return the public key corresponding to this certificate.
185     */
186    public abstract PublicKey getPublicKey();
187
188    /**
189     * Returns an alternate object to be serialized.
190     *
191     * @return the object to serialize.
192     * @throws ObjectStreamException
193     *             if the creation of the alternate object fails.
194     */
195    protected Object writeReplace() throws ObjectStreamException {
196        try {
197            return new CertificateRep(getType(), getEncoded());
198        } catch (CertificateEncodingException e) {
199            throw new NotSerializableException("Could not create serialization object: " + e);
200        }
201    }
202
203    /**
204     * The alternate {@code Serializable} class to be used for serialization and
205     * deserialization of {@code Certificate} objects.
206     */
207    protected static class CertificateRep implements Serializable {
208
209        private static final long serialVersionUID = -8563758940495660020L;
210        // The standard name of the certificate type
211        private final String type;
212        // The certificate data
213        private final byte[] data;
214
215        // Force default serialization to use writeUnshared/readUnshared
216        // for the certificate data
217        private static final ObjectStreamField[] serialPersistentFields = {
218             new ObjectStreamField("type", String.class),
219             new ObjectStreamField("data", byte[].class, true)
220        };
221
222        /**
223         * Creates a new {@code CertificateRep} instance with the specified
224         * certificate type and encoded data.
225         *
226         * @param type
227         *            the certificate type.
228         * @param data
229         *            the encoded data.
230         */
231        protected CertificateRep(String type, byte[] data) {
232            this.type = type;
233            this.data = data;
234        }
235
236        /**
237         * Deserializes a {@code Certificate} from a serialized {@code
238         * CertificateRep} object.
239         *
240         * @return the deserialized {@code Certificate}.
241         * @throws ObjectStreamException
242         *             if deserialization fails.
243         */
244        protected Object readResolve() throws ObjectStreamException {
245            try {
246                CertificateFactory cf = CertificateFactory.getInstance(type);
247                return cf.generateCertificate(new ByteArrayInputStream(data));
248            } catch (Throwable t) {
249                throw new NotSerializableException("Could not resolve certificate: " + t);
250            }
251        }
252    }
253}
254