1/*
2 * Copyright (c) 2007, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package java.security.cert;
27
28import java.io.ObjectInputStream;
29import java.io.ObjectOutputStream;
30import java.io.IOException;
31import java.util.Collections;
32import java.util.Date;
33import java.util.HashMap;
34import java.util.Map;
35import javax.security.auth.x500.X500Principal;
36
37import sun.security.util.ObjectIdentifier;
38import sun.security.x509.InvalidityDateExtension;
39
40/**
41 * An exception that indicates an X.509 certificate is revoked. A
42 * {@code CertificateRevokedException} contains additional information
43 * about the revoked certificate, such as the date on which the
44 * certificate was revoked and the reason it was revoked.
45 *
46 * @author Sean Mullan
47 * @since 1.7
48 * @see CertPathValidatorException
49 */
50public class CertificateRevokedException extends CertificateException {
51
52    private static final long serialVersionUID = 7839996631571608627L;
53
54    /**
55     * @serial the date on which the certificate was revoked
56     */
57    private Date revocationDate;
58    /**
59     * @serial the revocation reason
60     */
61    private final CRLReason reason;
62    /**
63     * @serial the {@code X500Principal} that represents the name of the
64     * authority that signed the certificate's revocation status information
65     */
66    private final X500Principal authority;
67
68    private transient Map<String, Extension> extensions;
69
70    /**
71     * Constructs a {@code CertificateRevokedException} with
72     * the specified revocation date, reason code, authority name, and map
73     * of extensions.
74     *
75     * @param revocationDate the date on which the certificate was revoked. The
76     *    date is copied to protect against subsequent modification.
77     * @param reason the revocation reason
78     * @param extensions a map of X.509 Extensions. Each key is an OID String
79     *    that maps to the corresponding Extension. The map is copied to
80     *    prevent subsequent modification.
81     * @param authority the {@code X500Principal} that represents the name
82     *    of the authority that signed the certificate's revocation status
83     *    information
84     * @throws NullPointerException if {@code revocationDate},
85     *    {@code reason}, {@code authority}, or
86     *    {@code extensions} is {@code null}
87     * @throws ClassCastException if {@code extensions} contains an incorrectly
88     *    typed key or value
89     */
90    public CertificateRevokedException(Date revocationDate, CRLReason reason,
91        X500Principal authority, Map<String, Extension> extensions) {
92        if (revocationDate == null || reason == null || authority == null ||
93            extensions == null) {
94            throw new NullPointerException();
95        }
96        this.revocationDate = new Date(revocationDate.getTime());
97        this.reason = reason;
98        this.authority = authority;
99        // make sure Map only contains correct types
100        this.extensions = Collections.checkedMap(new HashMap<>(),
101                                                 String.class, Extension.class);
102        this.extensions.putAll(extensions);
103    }
104
105    /**
106     * Returns the date on which the certificate was revoked. A new copy is
107     * returned each time the method is invoked to protect against subsequent
108     * modification.
109     *
110     * @return the revocation date
111     */
112    public Date getRevocationDate() {
113        return (Date) revocationDate.clone();
114    }
115
116    /**
117     * Returns the reason the certificate was revoked.
118     *
119     * @return the revocation reason
120     */
121    public CRLReason getRevocationReason() {
122        return reason;
123    }
124
125    /**
126     * Returns the name of the authority that signed the certificate's
127     * revocation status information.
128     *
129     * @return the {@code X500Principal} that represents the name of the
130     *     authority that signed the certificate's revocation status information
131     */
132    public X500Principal getAuthorityName() {
133        return authority;
134    }
135
136    /**
137     * Returns the invalidity date, as specified in the Invalidity Date
138     * extension of this {@code CertificateRevokedException}. The
139     * invalidity date is the date on which it is known or suspected that the
140     * private key was compromised or that the certificate otherwise became
141     * invalid. This implementation calls {@code getExtensions()} and
142     * checks the returned map for an entry for the Invalidity Date extension
143     * OID ("2.5.29.24"). If found, it returns the invalidity date in the
144     * extension; otherwise null. A new Date object is returned each time the
145     * method is invoked to protect against subsequent modification.
146     *
147     * @return the invalidity date, or {@code null} if not specified
148     */
149    public Date getInvalidityDate() {
150        Extension ext = getExtensions().get("2.5.29.24");
151        if (ext == null) {
152            return null;
153        } else {
154            try {
155                Date invalidity = InvalidityDateExtension.toImpl(ext).get("DATE");
156                return new Date(invalidity.getTime());
157            } catch (IOException ioe) {
158                return null;
159            }
160        }
161    }
162
163    /**
164     * Returns a map of X.509 extensions containing additional information
165     * about the revoked certificate, such as the Invalidity Date
166     * Extension. Each key is an OID String that maps to the corresponding
167     * Extension.
168     *
169     * @return an unmodifiable map of X.509 extensions, or an empty map
170     *    if there are no extensions
171     */
172    public Map<String, Extension> getExtensions() {
173        return Collections.unmodifiableMap(extensions);
174    }
175
176    @Override
177    public String getMessage() {
178        return "Certificate has been revoked, reason: "
179               + reason + ", revocation date: " + revocationDate
180               + ", authority: " + authority + ", extension OIDs: "
181               + extensions.keySet();
182    }
183
184    /**
185     * Serialize this {@code CertificateRevokedException} instance.
186     *
187     * @serialData the size of the extensions map (int), followed by all of
188     * the extensions in the map, in no particular order. For each extension,
189     * the following data is emitted: the OID String (Object), the criticality
190     * flag (boolean), the length of the encoded extension value byte array
191     * (int), and the encoded extension value bytes.
192     */
193    private void writeObject(ObjectOutputStream oos) throws IOException {
194        // Write out the non-transient fields
195        // (revocationDate, reason, authority)
196        oos.defaultWriteObject();
197
198        // Write out the size (number of mappings) of the extensions map
199        oos.writeInt(extensions.size());
200
201        // For each extension in the map, the following are emitted (in order):
202        // the OID String (Object), the criticality flag (boolean), the length
203        // of the encoded extension value byte array (int), and the encoded
204        // extension value byte array. The extensions themselves are emitted
205        // in no particular order.
206        for (Map.Entry<String, Extension> entry : extensions.entrySet()) {
207            Extension ext = entry.getValue();
208            oos.writeObject(ext.getId());
209            oos.writeBoolean(ext.isCritical());
210            byte[] extVal = ext.getValue();
211            oos.writeInt(extVal.length);
212            oos.write(extVal);
213        }
214    }
215
216    /**
217     * Deserialize the {@code CertificateRevokedException} instance.
218     */
219    private void readObject(ObjectInputStream ois)
220        throws IOException, ClassNotFoundException {
221        // Read in the non-transient fields
222        // (revocationDate, reason, authority)
223        ois.defaultReadObject();
224
225        // Defensively copy the revocation date
226        revocationDate = new Date(revocationDate.getTime());
227
228        // Read in the size (number of mappings) of the extensions map
229        // and create the extensions map
230        int size = ois.readInt();
231        if (size == 0) {
232            extensions = Collections.emptyMap();
233        } else {
234            extensions = new HashMap<>(size);
235        }
236
237        // Read in the extensions and put the mappings in the extensions map
238        for (int i = 0; i < size; i++) {
239            String oid = (String) ois.readObject();
240            boolean critical = ois.readBoolean();
241            int length = ois.readInt();
242            byte[] extVal = new byte[length];
243            ois.readFully(extVal);
244            Extension ext = sun.security.x509.Extension.newExtension
245                (new ObjectIdentifier(oid), critical, extVal);
246            extensions.put(oid, ext);
247        }
248    }
249}
250