1 /* 2 * Copyright (c) 1997, 2006, 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 sun.security.x509; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.lang.reflect.Constructor; 31 import java.lang.reflect.InvocationTargetException; 32 import java.security.cert.CRLException; 33 import java.security.cert.CertificateException; 34 import java.util.Collection; 35 import java.util.Collections; 36 import java.util.Enumeration; 37 import java.util.Map; 38 import java.util.TreeMap; 39 40 import sun.security.util.*; 41 import sun.misc.HexDumpEncoder; 42 43 /** 44 * This class defines the CRL Extensions. 45 * It is used for both CRL Extensions and CRL Entry Extensions, 46 * which are defined are follows: 47 * <pre> 48 * TBSCertList ::= SEQUENCE { 49 * version Version OPTIONAL, -- if present, must be v2 50 * signature AlgorithmIdentifier, 51 * issuer Name, 52 * thisUpdate Time, 53 * nextUpdate Time OPTIONAL, 54 * revokedCertificates SEQUENCE OF SEQUENCE { 55 * userCertificate CertificateSerialNumber, 56 * revocationDate Time, 57 * crlEntryExtensions Extensions OPTIONAL -- if present, must be v2 58 * } OPTIONAL, 59 * crlExtensions [0] EXPLICIT Extensions OPTIONAL -- if present, must be v2 60 * } 61 * </pre> 62 * 63 * @author Hemma Prafullchandra 64 */ 65 public class CRLExtensions { 66 67 private Map<String,Extension> map = Collections.synchronizedMap( 68 new TreeMap<String,Extension>()); 69 private boolean unsupportedCritExt = false; 70 71 /** 72 * Default constructor. 73 */ 74 public CRLExtensions() { } 75 76 /** 77 * Create the object, decoding the values from the passed DER stream. 78 * 79 * @param in the DerInputStream to read the Extension from, i.e. the 80 * sequence of extensions. 81 * @exception CRLException on decoding errors. 82 */ 83 public CRLExtensions(DerInputStream in) throws CRLException { 84 init(in); 85 } 86 87 // helper routine 88 private void init(DerInputStream derStrm) throws CRLException { 89 try { 90 DerInputStream str = derStrm; 91 92 byte nextByte = (byte)derStrm.peekByte(); 93 // check for context specific byte 0; skip it 94 if (((nextByte & 0x0c0) == 0x080) && 95 ((nextByte & 0x01f) == 0x000)) { 96 DerValue val = str.getDerValue(); 97 str = val.data; 98 } 99 100 DerValue[] exts = str.getSequence(5); 101 for (int i = 0; i < exts.length; i++) { 102 Extension ext = new Extension(exts[i]); 103 parseExtension(ext); 104 } 105 } catch (IOException e) { 106 throw new CRLException("Parsing error: " + e.toString()); 107 } 108 } 109 110 private static final Class[] PARAMS = {Boolean.class, Object.class}; 111 112 // Parse the encoded extension 113 private void parseExtension(Extension ext) throws CRLException { 114 try { 115 Class extClass = OIDMap.getClass(ext.getExtensionId()); 116 if (extClass == null) { // Unsupported extension 117 if (ext.isCritical()) 118 unsupportedCritExt = true; 119 if (map.put(ext.getExtensionId().toString(), ext) != null) 120 throw new CRLException("Duplicate extensions not allowed"); 121 return; 122 } 123 Constructor cons = ((Class<?>)extClass).getConstructor(PARAMS); 124 Object[] passed = new Object[] {Boolean.valueOf(ext.isCritical()), 125 ext.getExtensionValue()}; 126 CertAttrSet crlExt = (CertAttrSet)cons.newInstance(passed); 127 if (map.put(crlExt.getName(), (Extension)crlExt) != null) { 128 throw new CRLException("Duplicate extensions not allowed"); 129 } 130 } catch (InvocationTargetException invk) { 131 throw new CRLException(invk.getTargetException().getMessage()); 132 } catch (Exception e) { 133 throw new CRLException(e.toString()); 134 } 135 } 136 137 /** 138 * Encode the extensions in DER form to the stream. 139 * 140 * @param out the DerOutputStream to marshal the contents to. 141 * @param isExplicit the tag indicating whether this is an entry 142 * extension (false) or a CRL extension (true). 143 * @exception CRLException on encoding errors. 144 */ 145 public void encode(OutputStream out, boolean isExplicit) 146 throws CRLException { 147 try { 148 DerOutputStream extOut = new DerOutputStream(); 149 Collection<Extension> allExts = map.values(); 150 Object[] objs = allExts.toArray(); 151 152 for (int i = 0; i < objs.length; i++) { 153 if (objs[i] instanceof CertAttrSet) 154 ((CertAttrSet)objs[i]).encode(extOut); 155 else if (objs[i] instanceof Extension) 156 ((Extension)objs[i]).encode(extOut); 157 else 158 throw new CRLException("Illegal extension object"); 159 } 160 161 DerOutputStream seq = new DerOutputStream(); 162 seq.write(DerValue.tag_Sequence, extOut); 163 164 DerOutputStream tmp = new DerOutputStream(); 165 if (isExplicit) 166 tmp.write(DerValue.createTag(DerValue.TAG_CONTEXT, 167 true, (byte)0), seq); 168 else 169 tmp = seq; 170 171 out.write(tmp.toByteArray()); 172 } catch (IOException e) { 173 throw new CRLException("Encoding error: " + e.toString()); 174 } catch (CertificateException e) { 175 throw new CRLException("Encoding error: " + e.toString()); 176 } 177 } 178 179 /** 180 * Get the extension with this alias. 181 * 182 * @param alias the identifier string for the extension to retrieve. 183 */ 184 public Extension get(String alias) { 185 X509AttributeName attr = new X509AttributeName(alias); 186 String name; 187 String id = attr.getPrefix(); 188 if (id.equalsIgnoreCase(X509CertImpl.NAME)) { // fully qualified 189 int index = alias.lastIndexOf("."); 190 name = alias.substring(index + 1); 191 } else 192 name = alias; 193 return map.get(name); 194 } 195 196 /** 197 * Set the extension value with this alias. 198 * 199 * @param alias the identifier string for the extension to set. 200 * @param obj the Object to set the extension identified by the 201 * alias. 202 */ 203 public void set(String alias, Object obj) { 204 map.put(alias, (Extension)obj); 205 } 206 207 /** 208 * Delete the extension value with this alias. 209 * 210 * @param alias the identifier string for the extension to delete. 211 */ 212 public void delete(String alias) { 213 map.remove(alias); 214 } 215 216 /** 217 * Return an enumeration of the extensions. 218 * @return an enumeration of the extensions in this CRL. 219 */ 220 public Enumeration<Extension> getElements() { 221 return Collections.enumeration(map.values()); 222 } 223 224 /** 225 * Return a collection view of the extensions. 226 * @return a collection view of the extensions in this CRL. 227 */ 228 public Collection<Extension> getAllExtensions() { 229 return map.values(); 230 } 231 232 /** 233 * Return true if a critical extension is found that is 234 * not supported, otherwise return false. 235 */ 236 public boolean hasUnsupportedCriticalExtension() { 237 return unsupportedCritExt; 238 } 239 240 /** 241 * Compares this CRLExtensions for equality with the specified 242 * object. If the <code>other</code> object is an 243 * <code>instanceof</code> <code>CRLExtensions</code>, then 244 * all the entries are compared with the entries from this. 245 * 246 * @param other the object to test for equality with this CRLExtensions. 247 * @return true iff all the entries match that of the Other, 248 * false otherwise. 249 */ 250 public boolean equals(Object other) { 251 if (this == other) 252 return true; 253 if (!(other instanceof CRLExtensions)) 254 return false; 255 Collection<Extension> otherC = 256 ((CRLExtensions)other).getAllExtensions(); 257 Object[] objs = otherC.toArray(); 258 259 int len = objs.length; 260 if (len != map.size()) 261 return false; 262 263 Extension otherExt, thisExt; 264 String key = null; 265 for (int i = 0; i < len; i++) { 266 if (objs[i] instanceof CertAttrSet) 267 key = ((CertAttrSet)objs[i]).getName(); 268 otherExt = (Extension)objs[i]; 269 if (key == null) 270 key = otherExt.getExtensionId().toString(); 271 thisExt = map.get(key); 272 if (thisExt == null) 273 return false; 274 if (! thisExt.equals(otherExt)) 275 return false; 276 } 277 return true; 278 } 279 280 /** 281 * Returns a hashcode value for this CRLExtensions. 282 * 283 * @return the hashcode value. 284 */ 285 public int hashCode() { 286 return map.hashCode(); 287 } 288 289 /** 290 * Returns a string representation of this <tt>CRLExtensions</tt> object 291 * in the form of a set of entries, enclosed in braces and separated 292 * by the ASCII characters "<tt>, </tt>" (comma and space). 293 * <p>Overrides to <tt>toString</tt> method of <tt>Object</tt>. 294 * 295 * @return a string representation of this CRLExtensions. 296 */ 297 public String toString() { 298 return map.toString(); 299 } 300 } 301