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