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 26 package java.security.cert; 27 28 import java.io.ObjectInputStream; 29 import java.io.ObjectOutputStream; 30 import java.io.IOException; 31 import java.util.Collections; 32 import java.util.Date; 33 import java.util.HashMap; 34 import java.util.Map; 35 import javax.security.auth.x500.X500Principal; 36 37 import sun.security.util.ObjectIdentifier; 38 import 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 */ 50 public 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 */ CertificateRevokedException(Date revocationDate, CRLReason reason, X500Principal authority, Map<String, Extension> extensions)88 public CertificateRevokedException(Date revocationDate, CRLReason reason, 89 X500Principal authority, Map<String, Extension> extensions) { 90 if (revocationDate == null || reason == null || authority == null || 91 extensions == null) { 92 throw new NullPointerException(); 93 } 94 this.revocationDate = new Date(revocationDate.getTime()); 95 this.reason = reason; 96 this.authority = authority; 97 // make sure Map only contains correct types 98 this.extensions = Collections.checkedMap(new HashMap<>(), 99 String.class, Extension.class); 100 this.extensions.putAll(extensions); 101 } 102 103 /** 104 * Returns the date on which the certificate was revoked. A new copy is 105 * returned each time the method is invoked to protect against subsequent 106 * modification. 107 * 108 * @return the revocation date 109 */ getRevocationDate()110 public Date getRevocationDate() { 111 return (Date) revocationDate.clone(); 112 } 113 114 /** 115 * Returns the reason the certificate was revoked. 116 * 117 * @return the revocation reason 118 */ getRevocationReason()119 public CRLReason getRevocationReason() { 120 return reason; 121 } 122 123 /** 124 * Returns the name of the authority that signed the certificate's 125 * revocation status information. 126 * 127 * @return the {@code X500Principal} that represents the name of the 128 * authority that signed the certificate's revocation status information 129 */ getAuthorityName()130 public X500Principal getAuthorityName() { 131 return authority; 132 } 133 134 /** 135 * Returns the invalidity date, as specified in the Invalidity Date 136 * extension of this {@code CertificateRevokedException}. The 137 * invalidity date is the date on which it is known or suspected that the 138 * private key was compromised or that the certificate otherwise became 139 * invalid. This implementation calls {@code getExtensions()} and 140 * checks the returned map for an entry for the Invalidity Date extension 141 * OID ("2.5.29.24"). If found, it returns the invalidity date in the 142 * extension; otherwise null. A new Date object is returned each time the 143 * method is invoked to protect against subsequent modification. 144 * 145 * @return the invalidity date, or {@code null} if not specified 146 */ getInvalidityDate()147 public Date getInvalidityDate() { 148 Extension ext = getExtensions().get("2.5.29.24"); 149 if (ext == null) { 150 return null; 151 } else { 152 try { 153 Date invalidity = InvalidityDateExtension.toImpl(ext).get("DATE"); 154 return new Date(invalidity.getTime()); 155 } catch (IOException ioe) { 156 return null; 157 } 158 } 159 } 160 161 /** 162 * Returns a map of X.509 extensions containing additional information 163 * about the revoked certificate, such as the Invalidity Date 164 * Extension. Each key is an OID String that maps to the corresponding 165 * Extension. 166 * 167 * @return an unmodifiable map of X.509 extensions, or an empty map 168 * if there are no extensions 169 */ getExtensions()170 public Map<String, Extension> getExtensions() { 171 return Collections.unmodifiableMap(extensions); 172 } 173 174 @Override getMessage()175 public String getMessage() { 176 return "Certificate has been revoked, reason: " 177 + reason + ", revocation date: " + revocationDate 178 + ", authority: " + authority + ", extension OIDs: " 179 + extensions.keySet(); 180 } 181 182 /** 183 * Serialize this {@code CertificateRevokedException} instance. 184 * 185 * @serialData the size of the extensions map (int), followed by all of 186 * the extensions in the map, in no particular order. For each extension, 187 * the following data is emitted: the OID String (Object), the criticality 188 * flag (boolean), the length of the encoded extension value byte array 189 * (int), and the encoded extension value bytes. 190 */ writeObject(ObjectOutputStream oos)191 private void writeObject(ObjectOutputStream oos) throws IOException { 192 // Write out the non-transient fields 193 // (revocationDate, reason, authority) 194 oos.defaultWriteObject(); 195 196 // Write out the size (number of mappings) of the extensions map 197 oos.writeInt(extensions.size()); 198 199 // For each extension in the map, the following are emitted (in order): 200 // the OID String (Object), the criticality flag (boolean), the length 201 // of the encoded extension value byte array (int), and the encoded 202 // extension value byte array. The extensions themselves are emitted 203 // in no particular order. 204 for (Map.Entry<String, Extension> entry : extensions.entrySet()) { 205 Extension ext = entry.getValue(); 206 oos.writeObject(ext.getId()); 207 oos.writeBoolean(ext.isCritical()); 208 byte[] extVal = ext.getValue(); 209 oos.writeInt(extVal.length); 210 oos.write(extVal); 211 } 212 } 213 214 /** 215 * Deserialize the {@code CertificateRevokedException} instance. 216 */ readObject(ObjectInputStream ois)217 private void readObject(ObjectInputStream ois) 218 throws IOException, ClassNotFoundException { 219 // Read in the non-transient fields 220 // (revocationDate, reason, authority) 221 ois.defaultReadObject(); 222 223 // Defensively copy the revocation date 224 revocationDate = new Date(revocationDate.getTime()); 225 226 // Read in the size (number of mappings) of the extensions map 227 // and create the extensions map 228 int size = ois.readInt(); 229 if (size == 0) { 230 extensions = Collections.emptyMap(); 231 } else { 232 extensions = new HashMap<String, Extension>(size); 233 } 234 235 // Read in the extensions and put the mappings in the extensions map 236 for (int i = 0; i < size; i++) { 237 String oid = (String) ois.readObject(); 238 boolean critical = ois.readBoolean(); 239 int length = ois.readInt(); 240 byte[] extVal = new byte[length]; 241 ois.readFully(extVal); 242 Extension ext = sun.security.x509.Extension.newExtension 243 (new ObjectIdentifier(oid), critical, extVal); 244 extensions.put(oid, ext); 245 } 246 } 247 } 248