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