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
26package sun.security.pkcs;
27
28import java.io.IOException;
29import java.io.OutputStream;
30import java.security.cert.CertificateException;
31import java.util.Locale;
32import java.util.Date;
33import java.util.Hashtable;
34import sun.security.x509.CertificateExtensions;
35import sun.security.util.Debug;
36import sun.security.util.DerEncoder;
37import sun.security.util.DerValue;
38import sun.security.util.DerInputStream;
39import sun.security.util.DerOutputStream;
40import sun.security.util.ObjectIdentifier;
41import 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 */
180public 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}
879