View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, 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.security.cert.CertificateException;
31  import java.util.Locale;
32  import java.util.Date;
33  import java.util.Hashtable;
34  import sun.security.x509.CertificateExtensions;
35  import sun.security.util.Debug;
36  import sun.security.util.DerEncoder;
37  import sun.security.util.DerValue;
38  import sun.security.util.DerInputStream;
39  import sun.security.util.DerOutputStream;
40  import sun.security.util.ObjectIdentifier;
41  import sun.misc.HexDumpEncoder;
42  
43  /**
44   * Class supporting any PKCS9 attributes.
45   * Supports DER decoding/encoding and access to attribute values.
46   *
47   * <a name="classTable"><h3>Type/Class Table</h3></a>
48   * The following table shows the correspondence between
49   * PKCS9 attribute types and value component classes.
50   * For types not listed here, its name is the OID
51   * in string form, its value is a (single-valued)
52   * byte array that is the SET's encoding.
53   *
54   * <P>
55   * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
56   *
57   * <TR>
58   * <TH>Object Identifier</TH>
59   * <TH>Attribute Name</TH>
60   * <TH>Type</TH>
61   * <TH>Value Class</TH>
62   * </TR>
63   *
64   * <TR>
65   * <TD>1.2.840.113549.1.9.1</TD>
66   * <TD>EmailAddress</TD>
67   * <TD>Multi-valued</TD>
68   * <TD><code>String[]</code></TD>
69   * </TR>
70   *
71   * <TR>
72   * <TD>1.2.840.113549.1.9.2</TD>
73   * <TD>UnstructuredName</TD>
74   * <TD>Multi-valued</TD>
75   * <TD><code>String[]</code></TD>
76   * </TR>
77   *
78   * <TR>
79   * <TD>1.2.840.113549.1.9.3</TD>
80   * <TD>ContentType</TD>
81   * <TD>Single-valued</TD>
82   * <TD><code>ObjectIdentifier</code></TD>
83   * </TR>
84   *
85   * <TR>
86   * <TD>1.2.840.113549.1.9.4</TD>
87   * <TD>MessageDigest</TD>
88   * <TD>Single-valued</TD>
89   * <TD><code>byte[]</code></TD>
90   * </TR>
91   *
92   * <TR>
93   * <TD>1.2.840.113549.1.9.5</TD>
94   * <TD>SigningTime</TD>
95   * <TD>Single-valued</TD>
96   * <TD><code>Date</code></TD>
97   * </TR>
98   *
99   * <TR>
100  * <TD>1.2.840.113549.1.9.6</TD>
101  * <TD>Countersignature</TD>
102  * <TD>Multi-valued</TD>
103  * <TD><code>SignerInfo[]</code></TD>
104  * </TR>
105  *
106  * <TR>
107  * <TD>1.2.840.113549.1.9.7</TD>
108  * <TD>ChallengePassword</TD>
109  * <TD>Single-valued</TD>
110  * <TD><code>String</code></TD>
111  * </TR>
112  *
113  * <TR>
114  * <TD>1.2.840.113549.1.9.8</TD>
115  * <TD>UnstructuredAddress</TD>
116  * <TD>Single-valued</TD>
117  * <TD><code>String</code></TD>
118  * </TR>
119  *
120  * <TR>
121  * <TD>1.2.840.113549.1.9.9</TD>
122  * <TD>ExtendedCertificateAttributes</TD>
123  * <TD>Multi-valued</TD>
124  * <TD>(not supported)</TD>
125  * </TR>
126  *
127  * <TR>
128  * <TD>1.2.840.113549.1.9.10</TD>
129  * <TD>IssuerAndSerialNumber</TD>
130  * <TD>Single-valued</TD>
131  * <TD>(not supported)</TD>
132  * </TR>
133  *
134  * <TR>
135  * <TD>1.2.840.113549.1.9.{11,12}</TD>
136  * <TD>RSA DSI proprietary</TD>
137  * <TD>Single-valued</TD>
138  * <TD>(not supported)</TD>
139  * </TR>
140  *
141  * <TR>
142  * <TD>1.2.840.113549.1.9.13</TD>
143  * <TD>S/MIME unused assignment</TD>
144  * <TD>Single-valued</TD>
145  * <TD>(not supported)</TD>
146  * </TR>
147  *
148  * <TR>
149  * <TD>1.2.840.113549.1.9.14</TD>
150  * <TD>ExtensionRequest</TD>
151  * <TD>Single-valued</TD>
152  * <TD>CertificateExtensions</TD>
153  * </TR>
154  *
155  * <TR>
156  * <TD>1.2.840.113549.1.9.15</TD>
157  * <TD>SMIMECapability</TD>
158  * <TD>Single-valued</TD>
159  * <TD>(not supported)</TD>
160  * </TR>
161  *
162  * <TR>
163  * <TD>1.2.840.113549.1.9.16.2.12</TD>
164  * <TD>SigningCertificate</TD>
165  * <TD>Single-valued</TD>
166  * <TD>SigningCertificateInfo</TD>
167  * </TR>
168  *
169  * <TR>
170  * <TD>1.2.840.113549.1.9.16.2.14</TD>
171  * <TD>SignatureTimestampToken</TD>
172  * <TD>Single-valued</TD>
173  * <TD>byte[]</TD>
174  * </TR>
175  *
176  * </TABLE>
177  *
178  * @author Douglas Hoover
179  */
180 public class PKCS9Attribute implements DerEncoder {
181 
182     /* Are we debugging ? */
183     private static final Debug debug = Debug.getInstance("jar");
184 
185     /**
186      * Array of attribute OIDs defined in PKCS9, by number.
187      */
188     static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[18];
189 
190     private final static Class<?> BYTE_ARRAY_CLASS;
191 
192     static {   // static initializer for PKCS9_OIDS
193         for (int i = 1; i < PKCS9_OIDS.length - 2; i++) {
194             PKCS9_OIDS[i] =
195                 ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,i});
196         }
197         // Initialize SigningCertificate and SignatureTimestampToken
198         // separately (because their values are out of sequence)
199         PKCS9_OIDS[PKCS9_OIDS.length - 2] =
200             ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,16,2,12});
201         PKCS9_OIDS[PKCS9_OIDS.length - 1] =
202             ObjectIdentifier.newInternal(new int[]{1,2,840,113549,1,9,16,2,14});
203 
204         try {
205             BYTE_ARRAY_CLASS = Class.forName("[B");
206         } catch (ClassNotFoundException e) {
207             throw new ExceptionInInitializerError(e.toString());
208         }
209     }
210 
211     // first element [0] not used
212     public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1];
213     public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2];
214     public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3];
215     public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4];
216     public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5];
217     public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6];
218     public static final ObjectIdentifier CHALLENGE_PASSWORD_OID = PKCS9_OIDS[7];
219     public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID = PKCS9_OIDS[8];
220     public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID
221                                          = PKCS9_OIDS[9];
222     public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID = PKCS9_OIDS[10];
223     // [11], [12] are RSA DSI proprietary
224     // [13] ==> signingDescription, S/MIME, not used anymore
225     public static final ObjectIdentifier EXTENSION_REQUEST_OID = PKCS9_OIDS[14];
226     public static final ObjectIdentifier SMIME_CAPABILITY_OID = PKCS9_OIDS[15];
227     public static final ObjectIdentifier SIGNING_CERTIFICATE_OID = PKCS9_OIDS[16];
228     public static final ObjectIdentifier SIGNATURE_TIMESTAMP_TOKEN_OID =
229                                 PKCS9_OIDS[17];
230     public static final String EMAIL_ADDRESS_STR = "EmailAddress";
231     public static final String UNSTRUCTURED_NAME_STR = "UnstructuredName";
232     public static final String CONTENT_TYPE_STR = "ContentType";
233     public static final String MESSAGE_DIGEST_STR = "MessageDigest";
234     public static final String SIGNING_TIME_STR = "SigningTime";
235     public static final String COUNTERSIGNATURE_STR = "Countersignature";
236     public static final String CHALLENGE_PASSWORD_STR = "ChallengePassword";
237     public static final String UNSTRUCTURED_ADDRESS_STR = "UnstructuredAddress";
238     public static final String EXTENDED_CERTIFICATE_ATTRIBUTES_STR =
239                                "ExtendedCertificateAttributes";
240     public static final String ISSUER_SERIALNUMBER_STR = "IssuerAndSerialNumber";
241     // [11], [12] are RSA DSI proprietary
242     private static final String RSA_PROPRIETARY_STR = "RSAProprietary";
243     // [13] ==> signingDescription, S/MIME, not used anymore
244     private static final String SMIME_SIGNING_DESC_STR = "SMIMESigningDesc";
245     public static final String EXTENSION_REQUEST_STR = "ExtensionRequest";
246     public static final String SMIME_CAPABILITY_STR = "SMIMECapability";
247     public static final String SIGNING_CERTIFICATE_STR = "SigningCertificate";
248     public static final String SIGNATURE_TIMESTAMP_TOKEN_STR =
249                                 "SignatureTimestampToken";
250 
251     /**
252      * Hashtable mapping names and variant names of supported
253      * attributes to their OIDs. This table contains all name forms
254      * that occur in PKCS9, in lower case.
255      */
256     private static final Hashtable<String, ObjectIdentifier> NAME_OID_TABLE =
257         new Hashtable<String, ObjectIdentifier>(18);
258 
259     static { // static initializer for PCKS9_NAMES
260         NAME_OID_TABLE.put("emailaddress", PKCS9_OIDS[1]);
261         NAME_OID_TABLE.put("unstructuredname", PKCS9_OIDS[2]);
262         NAME_OID_TABLE.put("contenttype", PKCS9_OIDS[3]);
263         NAME_OID_TABLE.put("messagedigest", PKCS9_OIDS[4]);
264         NAME_OID_TABLE.put("signingtime", PKCS9_OIDS[5]);
265         NAME_OID_TABLE.put("countersignature", PKCS9_OIDS[6]);
266         NAME_OID_TABLE.put("challengepassword", PKCS9_OIDS[7]);
267         NAME_OID_TABLE.put("unstructuredaddress", PKCS9_OIDS[8]);
268         NAME_OID_TABLE.put("extendedcertificateattributes", PKCS9_OIDS[9]);
269         NAME_OID_TABLE.put("issuerandserialnumber", PKCS9_OIDS[10]);
270         NAME_OID_TABLE.put("rsaproprietary", PKCS9_OIDS[11]);
271         NAME_OID_TABLE.put("rsaproprietary", PKCS9_OIDS[12]);
272         NAME_OID_TABLE.put("signingdescription", PKCS9_OIDS[13]);
273         NAME_OID_TABLE.put("extensionrequest", PKCS9_OIDS[14]);
274         NAME_OID_TABLE.put("smimecapability", PKCS9_OIDS[15]);
275         NAME_OID_TABLE.put("signingcertificate", PKCS9_OIDS[16]);
276         NAME_OID_TABLE.put("signaturetimestamptoken", PKCS9_OIDS[17]);
277     };
278 
279     /**
280      * Hashtable mapping attribute OIDs defined in PKCS9 to the
281      * corresponding attribute value type.
282      */
283     private static final Hashtable<ObjectIdentifier, String> OID_NAME_TABLE =
284         new Hashtable<ObjectIdentifier, String>(16);
285     static {
286         OID_NAME_TABLE.put(PKCS9_OIDS[1], EMAIL_ADDRESS_STR);
287         OID_NAME_TABLE.put(PKCS9_OIDS[2], UNSTRUCTURED_NAME_STR);
288         OID_NAME_TABLE.put(PKCS9_OIDS[3], CONTENT_TYPE_STR);
289         OID_NAME_TABLE.put(PKCS9_OIDS[4], MESSAGE_DIGEST_STR);
290         OID_NAME_TABLE.put(PKCS9_OIDS[5], SIGNING_TIME_STR);
291         OID_NAME_TABLE.put(PKCS9_OIDS[6], COUNTERSIGNATURE_STR);
292         OID_NAME_TABLE.put(PKCS9_OIDS[7], CHALLENGE_PASSWORD_STR);
293         OID_NAME_TABLE.put(PKCS9_OIDS[8], UNSTRUCTURED_ADDRESS_STR);
294         OID_NAME_TABLE.put(PKCS9_OIDS[9], EXTENDED_CERTIFICATE_ATTRIBUTES_STR);
295         OID_NAME_TABLE.put(PKCS9_OIDS[10], ISSUER_SERIALNUMBER_STR);
296         OID_NAME_TABLE.put(PKCS9_OIDS[11], RSA_PROPRIETARY_STR);
297         OID_NAME_TABLE.put(PKCS9_OIDS[12], RSA_PROPRIETARY_STR);
298         OID_NAME_TABLE.put(PKCS9_OIDS[13], SMIME_SIGNING_DESC_STR);
299         OID_NAME_TABLE.put(PKCS9_OIDS[14], EXTENSION_REQUEST_STR);
300         OID_NAME_TABLE.put(PKCS9_OIDS[15], SMIME_CAPABILITY_STR);
301         OID_NAME_TABLE.put(PKCS9_OIDS[16], SIGNING_CERTIFICATE_STR);
302         OID_NAME_TABLE.put(PKCS9_OIDS[17], SIGNATURE_TIMESTAMP_TOKEN_STR);
303     }
304 
305     /**
306      * Acceptable ASN.1 tags for DER encodings of values of PKCS9
307      * attributes, by index in <code>PKCS9_OIDS</code>.
308      * Sets of acceptable tags are represented as arrays.
309      */
310     private static final Byte[][] PKCS9_VALUE_TAGS = {
311         null,
312         {new Byte(DerValue.tag_IA5String)},   // EMailAddress
313         {new Byte(DerValue.tag_IA5String),   // UnstructuredName
314          new Byte(DerValue.tag_PrintableString)},
315         {new Byte(DerValue.tag_ObjectId)},    // ContentType
316         {new Byte(DerValue.tag_OctetString)}, // MessageDigest
317         {new Byte(DerValue.tag_UtcTime)},     // SigningTime
318         {new Byte(DerValue.tag_Sequence)},    // Countersignature
319         {new Byte(DerValue.tag_PrintableString),
320          new Byte(DerValue.tag_T61String)},   // ChallengePassword
321         {new Byte(DerValue.tag_PrintableString),
322          new Byte(DerValue.tag_T61String)},   // UnstructuredAddress
323         {new Byte(DerValue.tag_SetOf)},       // ExtendedCertificateAttributes
324         {new Byte(DerValue.tag_Sequence)},    // issuerAndSerialNumber
325         null,
326         null,
327         null,
328         {new Byte(DerValue.tag_Sequence)},    // extensionRequest
329         {new Byte(DerValue.tag_Sequence)},    // SMIMECapability
330         {new Byte(DerValue.tag_Sequence)},    // SigningCertificate
331         {new Byte(DerValue.tag_Sequence)}     // SignatureTimestampToken
332     };
333 
334     private static final Class<?>[] VALUE_CLASSES = new Class<?>[18];
335 
336     static {
337         try {
338             Class<?> str = Class.forName("[Ljava.lang.String;");
339 
340             VALUE_CLASSES[0] = null;  // not used
341             VALUE_CLASSES[1] = str;   // EMailAddress
342             VALUE_CLASSES[2] = str;   // UnstructuredName
343             VALUE_CLASSES[3] =        // ContentType
344                 Class.forName("sun.security.util.ObjectIdentifier");
345             VALUE_CLASSES[4] = BYTE_ARRAY_CLASS; // MessageDigest (byte[])
346             VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime
347             VALUE_CLASSES[6] =        // Countersignature
348                 Class.forName("[Lsun.security.pkcs.SignerInfo;");
349             VALUE_CLASSES[7] =        // ChallengePassword
350                 Class.forName("java.lang.String");
351             VALUE_CLASSES[8] = str;   // UnstructuredAddress
352             VALUE_CLASSES[9] = null;  // ExtendedCertificateAttributes
353             VALUE_CLASSES[10] = null;  // IssuerAndSerialNumber
354             VALUE_CLASSES[11] = null;  // not used
355             VALUE_CLASSES[12] = null;  // not used
356             VALUE_CLASSES[13] = null;  // not used
357             VALUE_CLASSES[14] =        // ExtensionRequest
358                 Class.forName("sun.security.x509.CertificateExtensions");
359             VALUE_CLASSES[15] = null;  // not supported yet
360             VALUE_CLASSES[16] = null;  // not supported yet
361             VALUE_CLASSES[17] = BYTE_ARRAY_CLASS;  // SignatureTimestampToken
362         } catch (ClassNotFoundException e) {
363             throw new ExceptionInInitializerError(e.toString());
364         }
365     }
366 
367     /**
368      * Array indicating which PKCS9 attributes are single-valued,
369      * by index in <code>PKCS9_OIDS</code>.
370      */
371     private static final boolean[] SINGLE_VALUED = {
372       false,
373       false,   // EMailAddress
374       false,   // UnstructuredName
375       true,    // ContentType
376       true,    // MessageDigest
377       true,    // SigningTime
378       false,   // Countersignature
379       true,    // ChallengePassword
380       false,   // UnstructuredAddress
381       false,   // ExtendedCertificateAttributes
382       true,    // IssuerAndSerialNumber - not supported yet
383       false,   // not used
384       false,   // not used
385       false,   // not used
386       true,    // ExtensionRequest
387       true,    // SMIMECapability - not supported yet
388       true,    // SigningCertificate
389       true     // SignatureTimestampToken
390     };
391 
392     /**
393      * The OID of this attribute.
394      */
395     private ObjectIdentifier oid;
396 
397     /**
398      * The index of the OID of this attribute in <code>PKCS9_OIDS</code>,
399      * or -1 if it's unknown.
400      */
401     private int index;
402 
403     /**
404      * Value set of this attribute.  Its class is given by
405      * <code>VALUE_CLASSES[index]</code>. The SET itself
406      * as byte[] if unknown.
407      */
408     private Object value;
409 
410     /**
411      * Construct an attribute object from the attribute's OID and
412      * value.  If the attribute is single-valued, provide only one
413      * value.  If the attribute is multi-valued, provide an array
414      * containing all the values.
415      * Arrays of length zero are accepted, though probably useless.
416      *
417      * <P> The
418      * <a href=#classTable>table</a> gives the class that <code>value</code>
419      * must have for a given attribute.
420      *
421      * @exception IllegalArgumentException
422      * if the <code>value</code> has the wrong type.
423      */
424     public PKCS9Attribute(ObjectIdentifier oid, Object value)
425     throws IllegalArgumentException {
426         init(oid, value);
427     }
428 
429     /**
430      * Construct an attribute object from the attribute's name and
431      * value.  If the attribute is single-valued, provide only one
432      * value.  If the attribute is multi-valued, provide an array
433      * containing all the values.
434      * Arrays of length zero are accepted, though probably useless.
435      *
436      * <P> The
437      * <a href=#classTable>table</a> gives the class that <code>value</code>
438      * must have for a given attribute. Reasonable variants of these
439      * attributes are accepted; in particular, case does not matter.
440      *
441      * @exception IllegalArgumentException
442      * if the <code>name</code> is not recognized or the
443      * <code>value</code> has the wrong type.
444      */
445     public PKCS9Attribute(String name, Object value)
446     throws IllegalArgumentException {
447         ObjectIdentifier oid = getOID(name);
448 
449         if (oid == null)
450             throw new IllegalArgumentException(
451                        "Unrecognized attribute name " + name +
452                        " constructing PKCS9Attribute.");
453 
454         init(oid, value);
455     }
456 
457     private void init(ObjectIdentifier oid, Object value)
458         throws IllegalArgumentException {
459 
460         this.oid = oid;
461         index = indexOf(oid, PKCS9_OIDS, 1);
462         Class<?> clazz = index == -1 ? BYTE_ARRAY_CLASS: VALUE_CLASSES[index];
463         if (!clazz.isInstance(value)) {
464                 throw new IllegalArgumentException(
465                            "Wrong value class " +
466                            " for attribute " + oid +
467                            " constructing PKCS9Attribute; was " +
468                            value.getClass().toString() + ", should be " +
469                            clazz.toString());
470         }
471         this.value = value;
472     }
473 
474 
475     /**
476      * Construct a PKCS9Attribute from its encoding on an input
477      * stream.
478      *
479      * @param val the DerValue representing the DER encoding of the attribute.
480      * @exception IOException on parsing error.
481      */
482     public PKCS9Attribute(DerValue derVal) throws IOException {
483 
484         DerInputStream derIn = new DerInputStream(derVal.toByteArray());
485         DerValue[] val =  derIn.getSequence(2);
486 
487         if (derIn.available() != 0)
488             throw new IOException("Excess data parsing PKCS9Attribute");
489 
490         if (val.length != 2)
491             throw new IOException("PKCS9Attribute doesn't have two components");
492 
493         // get the oid
494         oid = val[0].getOID();
495         byte[] content = val[1].toByteArray();
496         DerValue[] elems = new DerInputStream(content).getSet(1);
497 
498         index = indexOf(oid, PKCS9_OIDS, 1);
499         if (index == -1) {
500             if (debug != null) {
501                 debug.println("Unsupported signer attribute: " + oid);
502             }
503             value = content;
504             return;
505         }
506 
507         // check single valued have only one value
508         if (SINGLE_VALUED[index] && elems.length > 1)
509             throwSingleValuedException();
510 
511         // check for illegal element tags
512         Byte tag;
513         for (int i=0; i < elems.length; i++) {
514             tag = new Byte(elems[i].tag);
515 
516             if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1)
517                 throwTagException(tag);
518         }
519 
520         switch (index) {
521         case 1:     // email address
522         case 2:     // unstructured name
523         case 8:     // unstructured address
524             { // open scope
525                 String[] values = new String[elems.length];
526 
527                 for (int i=0; i < elems.length; i++)
528                     values[i] = elems[i].getAsString();
529                 value = values;
530             } // close scope
531             break;
532 
533         case 3:     // content type
534             value = elems[0].getOID();
535             break;
536 
537         case 4:     // message digest
538             value = elems[0].getOctetString();
539             break;
540 
541         case 5:     // signing time
542             value = (new DerInputStream(elems[0].toByteArray())).getUTCTime();
543             break;
544 
545         case 6:     // countersignature
546             { // open scope
547                 SignerInfo[] values = new SignerInfo[elems.length];
548                 for (int i=0; i < elems.length; i++)
549                     values[i] =
550                         new SignerInfo(elems[i].toDerInputStream());
551                 value = values;
552             } // close scope
553             break;
554 
555         case 7:     // challenge password
556             value = elems[0].getAsString();
557             break;
558 
559         case 9:     // extended-certificate attribute -- not supported
560             throw new IOException("PKCS9 extended-certificate " +
561                                   "attribute not supported.");
562             // break unnecessary
563         case 10:    // issuerAndserialNumber attribute -- not supported
564             throw new IOException("PKCS9 IssuerAndSerialNumber" +
565                                   "attribute not supported.");
566             // break unnecessary
567         case 11:    // RSA DSI proprietary
568         case 12:    // RSA DSI proprietary
569             throw new IOException("PKCS9 RSA DSI attributes" +
570                                   "11 and 12, not supported.");
571             // break unnecessary
572         case 13:    // S/MIME unused attribute
573             throw new IOException("PKCS9 attribute #13 not supported.");
574             // break unnecessary
575 
576         case 14:     // ExtensionRequest
577             value = new CertificateExtensions(
578                        new DerInputStream(elems[0].toByteArray()));
579             break;
580 
581         case 15:     // SMIME-capability attribute -- not supported
582             throw new IOException("PKCS9 SMIMECapability " +
583                                   "attribute not supported.");
584             // break unnecessary
585         case 16:     // SigningCertificate attribute
586             value = new SigningCertificateInfo(elems[0].toByteArray());
587             break;
588 
589         case 17:     // SignatureTimestampToken attribute
590             value = elems[0].toByteArray();
591             break;
592         default: // can't happen
593         }
594     }
595 
596     /**
597      * Write the DER encoding of this attribute to an output stream.
598      *
599      * <P> N.B.: This method always encodes values of
600      * ChallengePassword and UnstructuredAddress attributes as ASN.1
601      * <code>PrintableString</code>s, without checking whether they
602      * should be encoded as <code>T61String</code>s.
603      */
604     public void derEncode(OutputStream out) throws IOException {
605         DerOutputStream temp = new DerOutputStream();
606         temp.putOID(oid);
607         switch (index) {
608         case -1:    // Unknown
609             temp.write((byte[])value);
610             break;
611         case 1:     // email address
612         case 2:     // unstructured name
613             { // open scope
614                 String[] values = (String[]) value;
615                 DerOutputStream[] temps = new
616                     DerOutputStream[values.length];
617 
618                 for (int i=0; i < values.length; i++) {
619                     temps[i] = new DerOutputStream();
620                     temps[i].putIA5String( values[i]);
621                 }
622                 temp.putOrderedSetOf(DerValue.tag_Set, temps);
623             } // close scope
624             break;
625 
626         case 3:     // content type
627             {
628                 DerOutputStream temp2 = new DerOutputStream();
629                 temp2.putOID((ObjectIdentifier) value);
630                 temp.write(DerValue.tag_Set, temp2.toByteArray());
631             }
632             break;
633 
634         case 4:     // message digest
635             {
636                 DerOutputStream temp2 = new DerOutputStream();
637                 temp2.putOctetString((byte[]) value);
638                 temp.write(DerValue.tag_Set, temp2.toByteArray());
639             }
640             break;
641 
642         case 5:     // signing time
643             {
644                 DerOutputStream temp2 = new DerOutputStream();
645                 temp2.putUTCTime((Date) value);
646                 temp.write(DerValue.tag_Set, temp2.toByteArray());
647             }
648             break;
649 
650         case 6:     // countersignature
651             temp.putOrderedSetOf(DerValue.tag_Set, (DerEncoder[]) value);
652             break;
653 
654         case 7:     // challenge password
655             {
656                 DerOutputStream temp2 = new DerOutputStream();
657                 temp2.putPrintableString((String) value);
658                 temp.write(DerValue.tag_Set, temp2.toByteArray());
659             }
660             break;
661 
662         case 8:     // unstructured address
663             { // open scope
664                 String[] values = (String[]) value;
665                 DerOutputStream[] temps = new
666                     DerOutputStream[values.length];
667 
668                 for (int i=0; i < values.length; i++) {
669                     temps[i] = new DerOutputStream();
670                     temps[i].putPrintableString(values[i]);
671                 }
672                 temp.putOrderedSetOf(DerValue.tag_Set, temps);
673             } // close scope
674             break;
675 
676         case 9:     // extended-certificate attribute -- not supported
677             throw new IOException("PKCS9 extended-certificate " +
678                                   "attribute not supported.");
679             // break unnecessary
680         case 10:    // issuerAndserialNumber attribute -- not supported
681             throw new IOException("PKCS9 IssuerAndSerialNumber" +
682                                   "attribute not supported.");
683             // break unnecessary
684         case 11:    // RSA DSI proprietary
685         case 12:    // RSA DSI proprietary
686             throw new IOException("PKCS9 RSA DSI attributes" +
687                                   "11 and 12, not supported.");
688             // break unnecessary
689         case 13:    // S/MIME unused attribute
690             throw new IOException("PKCS9 attribute #13 not supported.");
691             // break unnecessary
692 
693         case 14:     // ExtensionRequest
694             {
695                 DerOutputStream temp2 = new DerOutputStream();
696                 CertificateExtensions exts = (CertificateExtensions)value;
697                 try {
698                     exts.encode(temp2, true);
699                 } catch (CertificateException ex) {
700                     throw new IOException(ex.toString());
701                 }
702                 temp.write(DerValue.tag_Set, temp2.toByteArray());
703             }
704             break;
705         case 15:    // SMIMECapability
706             throw new IOException("PKCS9 attribute #15 not supported.");
707             // break unnecessary
708 
709         case 16:    // SigningCertificate
710             throw new IOException(
711                 "PKCS9 SigningCertificate attribute not supported.");
712             // break unnecessary
713 
714         case 17:    // SignatureTimestampToken
715             temp.write(DerValue.tag_Set, (byte[])value);
716             break;
717 
718         default: // can't happen
719         }
720 
721         DerOutputStream derOut = new DerOutputStream();
722         derOut.write(DerValue.tag_Sequence, temp.toByteArray());
723 
724         out.write(derOut.toByteArray());
725 
726     }
727 
728     /**
729      * Returns if the attribute is known. Unknown attributes can be created
730      * from DER encoding with unknown OIDs.
731      */
732     public boolean isKnown() {
733         return index != -1;
734     }
735 
736     /**
737      * Get the value of this attribute.  If the attribute is
738      * single-valued, return just the one value.  If the attribute is
739      * multi-valued, return an array containing all the values.
740      * It is possible for this array to be of length 0.
741      *
742      * <P> The
743      * <a href=#classTable>table</a> gives the class of the value returned,
744      * depending on the type of this attribute.
745      */
746     public Object getValue() {
747         return value;
748     }
749 
750     /**
751      * Show whether this attribute is single-valued.
752      */
753     public boolean isSingleValued() {
754         return index == -1 || SINGLE_VALUED[index];
755     }
756 
757     /**
758      *  Return the OID of this attribute.
759      */
760     public ObjectIdentifier getOID() {
761         return oid;
762     }
763 
764     /**
765      *  Return the name of this attribute.
766      */
767     public String getName() {
768         return index == -1 ?
769                 oid.toString() :
770                 OID_NAME_TABLE.get(PKCS9_OIDS[index]);
771     }
772 
773     /**
774      * Return the OID for a given attribute name or null if we don't recognize
775      * the name.
776      */
777     public static ObjectIdentifier getOID(String name) {
778         return NAME_OID_TABLE.get(name.toLowerCase(Locale.ENGLISH));
779     }
780 
781     /**
782      * Return the attribute name for a given OID or null if we don't recognize
783      * the oid.
784      */
785     public static String getName(ObjectIdentifier oid) {
786         return OID_NAME_TABLE.get(oid);
787     }
788 
789     /**
790      * Returns a string representation of this attribute.
791      */
792     public String toString() {
793         StringBuffer buf = new StringBuffer(100);
794 
795         buf.append("[");
796 
797         if (index == -1) {
798             buf.append(oid.toString());
799         } else {
800             buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index]));
801         }
802         buf.append(": ");
803 
804         if (index == -1 || SINGLE_VALUED[index]) {
805             if (value instanceof byte[]) { // special case for octet string
806                 HexDumpEncoder hexDump = new HexDumpEncoder();
807                 buf.append(hexDump.encodeBuffer((byte[]) value));
808             } else {
809                 buf.append(value.toString());
810             }
811             buf.append("]");
812             return buf.toString();
813         } else { // multi-valued
814             boolean first = true;
815             Object[] values = (Object[]) value;
816 
817             for (int j=0; j < values.length; j++) {
818                 if (first)
819                     first = false;
820                 else
821                     buf.append(", ");
822 
823                 buf.append(values[j].toString());
824             }
825             return buf.toString();
826         }
827     }
828 
829     /**
830      * Beginning the search at <code>start</code>, find the first
831      * index <code>i</code> such that <code>a[i] = obj</code>.
832      *
833      * @return the index, if found, and -1 otherwise.
834      */
835     static int indexOf(Object obj, Object[] a, int start) {
836         for (int i=start; i < a.length; i++) {
837             if (obj.equals(a[i])) return i;
838         }
839         return -1;
840     }
841 
842     /**
843      * Throw an exception when there are multiple values for
844      * a single-valued attribute.
845      */
846     private void throwSingleValuedException() throws IOException {
847         throw new IOException("Single-value attribute " +
848                               oid + " (" + getName() + ")" +
849                               " has multiple values.");
850     }
851 
852     /**
853      * Throw an exception when the tag on a value encoding is
854      * wrong for the attribute whose value it is. This method
855      * will only be called for known tags.
856      */
857     private void throwTagException(Byte tag)
858     throws IOException {
859         Byte[] expectedTags = PKCS9_VALUE_TAGS[index];
860         StringBuffer msg = new StringBuffer(100);
861         msg.append("Value of attribute ");
862         msg.append(oid.toString());
863         msg.append(" (");
864         msg.append(getName());
865         msg.append(") has wrong tag: ");
866         msg.append(tag.toString());
867         msg.append(".  Expected tags: ");
868 
869         msg.append(expectedTags[0].toString());
870 
871         for (int i = 1; i < expectedTags.length; i++) {
872             msg.append(", ");
873             msg.append(expectedTags[i].toString());
874         }
875         msg.append(".");
876         throw new IOException(msg.toString());
877     }
878 }