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