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