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.pkcs; 27 28 import java.io.IOException; 29 import java.io.OutputStream; 30 import java.util.Hashtable; 31 import sun.security.util.DerEncoder; 32 import sun.security.util.DerValue; 33 import sun.security.util.DerInputStream; 34 import sun.security.util.DerOutputStream; 35 import sun.security.util.ObjectIdentifier; 36 37 /** 38 * A set of attributes of class PKCS9Attribute. 39 * 40 * @author Douglas Hoover 41 */ 42 public class PKCS9Attributes { 43 /** 44 * Attributes in this set indexed by OID. 45 */ 46 private final Hashtable<ObjectIdentifier, PKCS9Attribute> attributes = 47 new Hashtable<ObjectIdentifier, PKCS9Attribute>(3); 48 49 /** 50 * The keys of this hashtable are the OIDs of permitted attributes. 51 */ 52 private final Hashtable<ObjectIdentifier, ObjectIdentifier> permittedAttributes; 53 54 /** 55 * The DER encoding of this attribute set. The tag byte must be 56 * DerValue.tag_SetOf. 57 */ 58 private final byte[] derEncoding; 59 60 /* 61 * Contols how attributes, which are not recognized by the PKCS9Attribute 62 * class, are handled during parsing. 63 */ 64 private boolean ignoreUnsupportedAttributes = false; 65 66 /** 67 * Construct a set of PKCS9 Attributes from its 68 * DER encoding on a DerInputStream, accepting only attributes 69 * with OIDs on the given 70 * list. If the array is null, accept all attributes supported by 71 * class PKCS9Attribute. 72 * 73 * @param permittedAttributes 74 * Array of attribute OIDs that will be accepted. 75 * @param in 76 * the contents of the DER encoding of the attribute set. 77 * 78 * @exception IOException 79 * on i/o error, encoding syntax error, unacceptable or 80 * unsupported attribute, or duplicate attribute. 81 * 82 * @see PKCS9Attribute 83 */ PKCS9Attributes(ObjectIdentifier[] permittedAttributes, DerInputStream in)84 public PKCS9Attributes(ObjectIdentifier[] permittedAttributes, 85 DerInputStream in) throws IOException { 86 if (permittedAttributes != null) { 87 this.permittedAttributes = 88 new Hashtable<>(permittedAttributes.length); 89 90 for (int i = 0; i < permittedAttributes.length; i++) 91 this.permittedAttributes.put(permittedAttributes[i], 92 permittedAttributes[i]); 93 } else { 94 this.permittedAttributes = null; 95 } 96 97 // derEncoding initialized in <code>decode()</code> 98 derEncoding = decode(in); 99 } 100 101 /** 102 * Construct a set of PKCS9 Attributes from the contents of its 103 * DER encoding on a DerInputStream. Accept all attributes 104 * supported by class PKCS9Attribute and reject any unsupported 105 * attributes. 106 * 107 * @param in the contents of the DER encoding of the attribute set. 108 * @exception IOException 109 * on i/o error, encoding syntax error, or unsupported or 110 * duplicate attribute. 111 * 112 * @see PKCS9Attribute 113 */ PKCS9Attributes(DerInputStream in)114 public PKCS9Attributes(DerInputStream in) throws IOException { 115 this(in, false); 116 } 117 118 /** 119 * Construct a set of PKCS9 Attributes from the contents of its 120 * DER encoding on a DerInputStream. Accept all attributes 121 * supported by class PKCS9Attribute and ignore any unsupported 122 * attributes, if directed. 123 * 124 * @param in the contents of the DER encoding of the attribute set. 125 * @param ignoreUnsupportedAttributes If true then any attributes 126 * not supported by the PKCS9Attribute class are ignored. Otherwise 127 * unsupported attributes cause an exception to be thrown. 128 * @exception IOException 129 * on i/o error, encoding syntax error, or unsupported or 130 * duplicate attribute. 131 * 132 * @see PKCS9Attribute 133 */ PKCS9Attributes(DerInputStream in, boolean ignoreUnsupportedAttributes)134 public PKCS9Attributes(DerInputStream in, 135 boolean ignoreUnsupportedAttributes) throws IOException { 136 137 this.ignoreUnsupportedAttributes = ignoreUnsupportedAttributes; 138 // derEncoding initialized in <code>decode()</code> 139 derEncoding = decode(in); 140 permittedAttributes = null; 141 } 142 143 /** 144 * Construct a set of PKCS9 Attributes from the given array of 145 * PKCS9 attributes. 146 * DER encoding on a DerInputStream. All attributes in 147 * <code>attribs</code> must be 148 * supported by class PKCS9Attribute. 149 * 150 * @exception IOException 151 * on i/o error, encoding syntax error, or unsupported or 152 * duplicate attribute. 153 * 154 * @see PKCS9Attribute 155 */ PKCS9Attributes(PKCS9Attribute[] attribs)156 public PKCS9Attributes(PKCS9Attribute[] attribs) 157 throws IllegalArgumentException, IOException { 158 ObjectIdentifier oid; 159 for (int i=0; i < attribs.length; i++) { 160 oid = attribs[i].getOID(); 161 if (attributes.containsKey(oid)) 162 throw new IllegalArgumentException( 163 "PKCSAttribute " + attribs[i].getOID() + 164 " duplicated while constructing " + 165 "PKCS9Attributes."); 166 167 attributes.put(oid, attribs[i]); 168 } 169 derEncoding = generateDerEncoding(); 170 permittedAttributes = null; 171 } 172 173 174 /** 175 * Decode this set of PKCS9 attributes from the contents of its 176 * DER encoding. Ignores unsupported attributes when directed. 177 * 178 * @param in 179 * the contents of the DER encoding of the attribute set. 180 * 181 * @exception IOException 182 * on i/o error, encoding syntax error, unacceptable or 183 * unsupported attribute, or duplicate attribute. 184 */ decode(DerInputStream in)185 private byte[] decode(DerInputStream in) throws IOException { 186 187 DerValue val = in.getDerValue(); 188 189 // save the DER encoding with its proper tag byte. 190 byte[] derEncoding = val.toByteArray(); 191 derEncoding[0] = DerValue.tag_SetOf; 192 193 DerInputStream derIn = new DerInputStream(derEncoding); 194 DerValue[] derVals = derIn.getSet(3,true); 195 196 PKCS9Attribute attrib; 197 ObjectIdentifier oid; 198 boolean reuseEncoding = true; 199 200 for (int i=0; i < derVals.length; i++) { 201 202 try { 203 attrib = new PKCS9Attribute(derVals[i]); 204 205 } catch (ParsingException e) { 206 if (ignoreUnsupportedAttributes) { 207 reuseEncoding = false; // cannot reuse supplied DER encoding 208 continue; // skip 209 } else { 210 throw e; 211 } 212 } 213 oid = attrib.getOID(); 214 215 if (attributes.get(oid) != null) 216 throw new IOException("Duplicate PKCS9 attribute: " + oid); 217 218 if (permittedAttributes != null && 219 !permittedAttributes.containsKey(oid)) 220 throw new IOException("Attribute " + oid + 221 " not permitted in this attribute set"); 222 223 attributes.put(oid, attrib); 224 } 225 return reuseEncoding ? derEncoding : generateDerEncoding(); 226 } 227 228 /** 229 * Put the DER encoding of this PKCS9 attribute set on an 230 * DerOutputStream, tagged with the given implicit tag. 231 * 232 * @param tag the implicit tag to use in the DER encoding. 233 * @param out the output stream on which to put the DER encoding. 234 * 235 * @exception IOException on output error. 236 */ encode(byte tag, OutputStream out)237 public void encode(byte tag, OutputStream out) throws IOException { 238 out.write(tag); 239 out.write(derEncoding, 1, derEncoding.length -1); 240 } 241 generateDerEncoding()242 private byte[] generateDerEncoding() throws IOException { 243 DerOutputStream out = new DerOutputStream(); 244 Object[] attribVals = attributes.values().toArray(); 245 246 out.putOrderedSetOf(DerValue.tag_SetOf, 247 castToDerEncoder(attribVals)); 248 return out.toByteArray(); 249 } 250 251 /** 252 * Return the DER encoding of this attribute set, tagged with 253 * DerValue.tag_SetOf. 254 */ getDerEncoding()255 public byte[] getDerEncoding() throws IOException { 256 return derEncoding.clone(); 257 258 } 259 260 /** 261 * Get an attribute from this set. 262 */ getAttribute(ObjectIdentifier oid)263 public PKCS9Attribute getAttribute(ObjectIdentifier oid) { 264 return attributes.get(oid); 265 } 266 267 /** 268 * Get an attribute from this set. 269 */ getAttribute(String name)270 public PKCS9Attribute getAttribute(String name) { 271 return attributes.get(PKCS9Attribute.getOID(name)); 272 } 273 274 275 /** 276 * Get an array of all attributes in this set, in order of OID. 277 */ getAttributes()278 public PKCS9Attribute[] getAttributes() { 279 PKCS9Attribute[] attribs = new PKCS9Attribute[attributes.size()]; 280 281 int j = 0; 282 for (int i=1; i < PKCS9Attribute.PKCS9_OIDS.length && 283 j < attribs.length; i++) { 284 if (PKCS9Attribute.PKCS9_OIDS[i] == null) { 285 continue; 286 } 287 attribs[j] = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]); 288 289 if (attribs[j] != null) 290 j++; 291 } 292 return attribs; 293 } 294 295 /** 296 * Get an attribute value by OID. 297 */ getAttributeValue(ObjectIdentifier oid)298 public Object getAttributeValue(ObjectIdentifier oid) 299 throws IOException { 300 try { 301 Object value = getAttribute(oid).getValue(); 302 return value; 303 } catch (NullPointerException ex) { 304 throw new IOException("No value found for attribute " + oid); 305 } 306 307 } 308 309 /** 310 * Get an attribute value by type name. 311 */ getAttributeValue(String name)312 public Object getAttributeValue(String name) throws IOException { 313 ObjectIdentifier oid = PKCS9Attribute.getOID(name); 314 315 if (oid == null) 316 throw new IOException("Attribute name " + name + 317 " not recognized or not supported."); 318 319 return getAttributeValue(oid); 320 } 321 322 323 /** 324 * Returns the PKCS9 block in a printable string form. 325 */ toString()326 public String toString() { 327 StringBuilder sb = new StringBuilder(200); 328 sb.append("PKCS9 Attributes: [\n\t"); 329 330 PKCS9Attribute value; 331 332 boolean first = true; 333 for (int i = 1; i < PKCS9Attribute.PKCS9_OIDS.length; i++) { 334 if (PKCS9Attribute.PKCS9_OIDS[i] == null) { 335 continue; 336 } 337 value = getAttribute(PKCS9Attribute.PKCS9_OIDS[i]); 338 339 if (value == null) continue; 340 341 // we have a value; print it 342 if (first) 343 first = false; 344 else 345 sb.append(";\n\t"); 346 347 sb.append(value); 348 } 349 350 sb.append("\n\t] (end PKCS9 Attributes)"); 351 352 return sb.toString(); 353 } 354 355 /** 356 * Cast an object array whose components are 357 * <code>DerEncoder</code>s to <code>DerEncoder[]</code>. 358 */ castToDerEncoder(Object[] objs)359 static DerEncoder[] castToDerEncoder(Object[] objs) { 360 361 DerEncoder[] encoders = new DerEncoder[objs.length]; 362 363 for (int i=0; i < encoders.length; i++) 364 encoders[i] = (DerEncoder) objs[i]; 365 366 return encoders; 367 } 368 } 369