View Javadoc
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       */
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      */
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      */
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      */
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      */
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      */
238     public void encode(byte tag, OutputStream out) throws IOException {
239         out.write(tag);
240         out.write(derEncoding, 1, derEncoding.length -1);
241     }
242 
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      */
256     public byte[] getDerEncoding() throws IOException {
257         return derEncoding.clone();
258 
259     }
260 
261     /**
262      * Get an attribute from this set.
263      */
264     public PKCS9Attribute getAttribute(ObjectIdentifier oid) {
265         return attributes.get(oid);
266     }
267 
268     /**
269      * Get an attribute from this set.
270      */
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      */
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      */
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      */
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      */
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      */
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 }