1/*
2 * Copyright (c) 2007, 2010, 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 java.util.Map.Entry;
36import javax.security.auth.x500.X500Principal;
37
38import sun.security.util.ObjectIdentifier;
39import sun.security.x509.InvalidityDateExtension;
40
41/**
42 * An exception that indicates an X.509 certificate is revoked. A
43 * <code>CertificateRevokedException</code> contains additional information
44 * about the revoked certificate, such as the date on which the
45 * certificate was revoked and the reason it was revoked.
46 *
47 * @author Sean Mullan
48 * @since 1.7
49 * @see CertPathValidatorException
50 */
51public class CertificateRevokedException extends CertificateException {
52
53    private static final long serialVersionUID = 7839996631571608627L;
54
55    /**
56     * @serial the date on which the certificate was revoked
57     */
58    private Date revocationDate;
59    /**
60     * @serial the revocation reason
61     */
62    private final CRLReason reason;
63    /**
64     * @serial the <code>X500Principal</code> that represents the name of the
65     * authority that signed the certificate's revocation status information
66     */
67    private final X500Principal authority;
68
69    private transient Map<String, Extension> extensions;
70
71    /**
72     * Constructs a <code>CertificateRevokedException</code> with
73     * the specified revocation date, reason code, authority name, and map
74     * of extensions.
75     *
76     * @param revocationDate the date on which the certificate was revoked. The
77     *    date is copied to protect against subsequent modification.
78     * @param reason the revocation reason
79     * @param extensions a map of X.509 Extensions. Each key is an OID String
80     *    that maps to the corresponding Extension. The map is copied to
81     *    prevent subsequent modification.
82     * @param authority the <code>X500Principal</code> that represents the name
83     *    of the authority that signed the certificate's revocation status
84     *    information
85     * @throws NullPointerException if <code>revocationDate</code>,
86     *    <code>reason</code>, <code>authority</code>, or
87     *    <code>extensions</code> is <code>null</code>
88     */
89    public CertificateRevokedException(Date revocationDate, CRLReason reason,
90        X500Principal authority, Map<String, Extension> extensions) {
91        if (revocationDate == null || reason == null || authority == null ||
92            extensions == null) {
93            throw new NullPointerException();
94        }
95        this.revocationDate = new Date(revocationDate.getTime());
96        this.reason = reason;
97        this.authority = authority;
98        this.extensions = new HashMap(extensions);
99    }
100
101    /**
102     * Returns the date on which the certificate was revoked. A new copy is
103     * returned each time the method is invoked to protect against subsequent
104     * modification.
105     *
106     * @return the revocation date
107     */
108    public Date getRevocationDate() {
109        return (Date) revocationDate.clone();
110    }
111
112    /**
113     * Returns the reason the certificate was revoked.
114     *
115     * @return the revocation reason
116     */
117    public CRLReason getRevocationReason() {
118        return reason;
119    }
120
121    /**
122     * Returns the name of the authority that signed the certificate's
123     * revocation status information.
124     *
125     * @return the <code>X500Principal</code> that represents the name of the
126     *     authority that signed the certificate's revocation status information
127     */
128    public X500Principal getAuthorityName() {
129        return authority;
130    }
131
132    /**
133     * Returns the invalidity date, as specifed in the Invalidity Date
134     * extension of this <code>CertificateRevokedException</code>. The
135     * invalidity date is the date on which it is known or suspected that the
136     * private key was compromised or that the certificate otherwise became
137     * invalid. This implementation calls <code>getExtensions()</code> and
138     * checks the returned map for an entry for the Invalidity Date extension
139     * OID ("2.5.29.24"). If found, it returns the invalidity date in the
140     * extension; otherwise null. A new Date object is returned each time the
141     * method is invoked to protect against subsequent modification.
142     *
143     * @return the invalidity date, or <code>null</code> if not specified
144     */
145    public Date getInvalidityDate() {
146        Extension ext = getExtensions().get("2.5.29.24");
147        if (ext == null) {
148            return null;
149        } else {
150            try {
151                Date invalidity =
152                    (Date) InvalidityDateExtension.toImpl(ext).get("DATE");
153                return new Date(invalidity.getTime());
154            } catch (IOException ioe) {
155                return null;
156            }
157        }
158    }
159
160    /**
161     * Returns a map of X.509 extensions containing additional information
162     * about the revoked certificate, such as the Invalidity Date
163     * Extension. Each key is an OID String that maps to the corresponding
164     * Extension.
165     *
166     * @return an unmodifiable map of X.509 extensions, or an empty map
167     *    if there are no extensions
168     */
169    public Map<String, Extension> getExtensions() {
170        return Collections.unmodifiableMap(extensions);
171    }
172
173    @Override
174    public String getMessage() {
175        return "Certificate has been revoked, reason: "
176               + reason + ", revocation date: " + revocationDate
177               + ", authority: " + authority + ", extensions: " + extensions;
178    }
179
180    /**
181     * Serialize this <code>CertificateRevokedException</code> instance.
182     *
183     * @serialData the size of the extensions map (int), followed by all of
184     * the extensions in the map, in no particular order. For each extension,
185     * the following data is emitted: the OID String (Object), the criticality
186     * flag (boolean), the length of the encoded extension value byte array
187     * (int), and the encoded extension value bytes.
188     */
189    private void writeObject(ObjectOutputStream oos) throws IOException {
190        // Write out the non-transient fields
191        // (revocationDate, reason, authority)
192        oos.defaultWriteObject();
193
194        // Write out the size (number of mappings) of the extensions map
195        oos.writeInt(extensions.size());
196
197        // For each extension in the map, the following are emitted (in order):
198        // the OID String (Object), the criticality flag (boolean), the length
199        // of the encoded extension value byte array (int), and the encoded
200        // extension value byte array. The extensions themselves are emitted
201        // in no particular order.
202        for (Map.Entry<String, Extension> entry : extensions.entrySet()) {
203            Extension ext = entry.getValue();
204            oos.writeObject(ext.getId());
205            oos.writeBoolean(ext.isCritical());
206            byte[] extVal = ext.getValue();
207            oos.writeInt(extVal.length);
208            oos.write(extVal);
209        }
210    }
211
212    /**
213     * Deserialize the <code>CertificateRevokedException</code> instance.
214     */
215    private void readObject(ObjectInputStream ois)
216        throws IOException, ClassNotFoundException {
217        // Read in the non-transient fields
218        // (revocationDate, reason, authority)
219        ois.defaultReadObject();
220
221        // Defensively copy the revocation date
222        revocationDate = new Date(revocationDate.getTime());
223
224        // Read in the size (number of mappings) of the extensions map
225        // and create the extensions map
226        int size = ois.readInt();
227        if (size == 0) {
228            extensions = Collections.emptyMap();
229        } else {
230            extensions = new HashMap<String, Extension>(size);
231        }
232
233        // Read in the extensions and put the mappings in the extensions map
234        for (int i = 0; i < size; i++) {
235            String oid = (String) ois.readObject();
236            boolean critical = ois.readBoolean();
237            int length = ois.readInt();
238            byte[] extVal = new byte[length];
239            ois.readFully(extVal);
240            Extension ext = sun.security.x509.Extension.newExtension
241                (new ObjectIdentifier(oid), critical, extVal);
242            extensions.put(oid, ext);
243        }
244    }
245}
246