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