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