001: /*
002: * @(#)PKCS9Attribute.java 1.14 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.pkcs;
029:
030: import java.io.IOException;
031: import java.io.OutputStream;
032: import java.security.cert.CertificateException;
033: import java.util.Date;
034: import java.util.Hashtable;
035: import sun.security.x509.CertificateExtensions;
036: import sun.security.util.DerEncoder;
037: import sun.security.util.DerValue;
038: import sun.security.util.DerInputStream;
039: import sun.security.util.DerOutputStream;
040: import sun.security.util.ObjectIdentifier;
041: import sun.misc.HexDumpEncoder;
042:
043: /**
044: * Class supporting any PKCS9 attributes.
045: * Supports DER decoding and access to attribute values, but not
046: * DER encoding or setting of values.
047: *
048: * <a name="classTable"><h3>Type/Class Table</h3></a>
049: * The following table shows the correspondence between
050: * PKCS9 attribute types and value component classes.
051: *
052: * <P>
053: * <TABLE BORDER CELLPADDING=8 ALIGN=CENTER>
054: *
055: * <TR>
056: * <TH>Object Identifier</TH>
057: * <TH>Attribute Name</TH>
058: * <TH>Type</TH>
059: * <TH>Value Class</TH>
060: * </TR>
061: *
062: * <TR>
063: * <TD>1.2.840.113549.1.9.1</TD>
064: * <TD>EmailAddress</TD>
065: * <TD>Multi-valued</TD>
066: * <TD><code>String[]</code></TD>
067: * </TR>
068: *
069: * <TR>
070: * <TD>1.2.840.113549.1.9.2</TD>
071: * <TD>UnstructuredName</TD>
072: * <TD>Multi-valued</TD>
073: * <TD><code>String[]</code></TD>
074: * </TR>
075: *
076: * <TR>
077: * <TD>1.2.840.113549.1.9.3</TD>
078: * <TD>ContentType</TD>
079: * <TD>Single-valued</TD>
080: * <TD><code>ObjectIdentifier</code></TD>
081: * </TR>
082: *
083: * <TR>
084: * <TD>1.2.840.113549.1.9.4</TD>
085: * <TD>MessageDigest</TD>
086: * <TD>Single-valued</TD>
087: * <TD><code>byte[]</code></TD>
088: * </TR>
089: *
090: * <TR>
091: * <TD>1.2.840.113549.1.9.5</TD>
092: * <TD>SigningTime</TD>
093: * <TD>Single-valued</TD>
094: * <TD><code>Date</code></TD>
095: * </TR>
096: *
097: * <TR>
098: * <TD>1.2.840.113549.1.9.6</TD>
099: * <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: * </TABLE>
161: *
162: * @version 1.7 02/02/00
163: * @author Douglas Hoover
164: */
165: public class PKCS9Attribute implements DerEncoder {
166:
167: /*
168: * OIDs of PKCS #9 attribute types.
169: */
170: private static final String RSADSI_str = "1.2.840.113549";
171: private static final String PKCS_str = RSADSI_str + ".1";
172: private static final String PKCS9_str = PKCS_str + ".9";
173:
174: /**
175: * Array of attribute OIDs defined in PKCS9, by number.
176: */
177: static final ObjectIdentifier[] PKCS9_OIDS = new ObjectIdentifier[16];
178:
179: static { // static initializer for PKCS9_OIDS
180: try {
181: for (int i = 1; i < PKCS9_OIDS.length; i++) {
182: PKCS9_OIDS[i] = new ObjectIdentifier(PKCS9_str + "."
183: + i);
184: }
185: } catch (IOException ioe) {
186: // Should not happen
187: }
188: }
189:
190: // first element [0] not used
191: public static final ObjectIdentifier EMAIL_ADDRESS_OID = PKCS9_OIDS[1];
192: public static final ObjectIdentifier UNSTRUCTURED_NAME_OID = PKCS9_OIDS[2];
193: public static final ObjectIdentifier CONTENT_TYPE_OID = PKCS9_OIDS[3];
194: public static final ObjectIdentifier MESSAGE_DIGEST_OID = PKCS9_OIDS[4];
195: public static final ObjectIdentifier SIGNING_TIME_OID = PKCS9_OIDS[5];
196: public static final ObjectIdentifier COUNTERSIGNATURE_OID = PKCS9_OIDS[6];
197: public static final ObjectIdentifier CHALLENGE_PASSWORD_OID = PKCS9_OIDS[7];
198: public static final ObjectIdentifier UNSTRUCTURED_ADDRESS_OID = PKCS9_OIDS[8];
199: public static final ObjectIdentifier EXTENDED_CERTIFICATE_ATTRIBUTES_OID = PKCS9_OIDS[9];
200: public static final ObjectIdentifier ISSUER_SERIALNUMBER_OID = PKCS9_OIDS[10];
201: // [11], [12] are RSA DSI proprietary
202: // [13] ==> signingDescription, S/MIME, not used anymore
203: public static final ObjectIdentifier EXTENSION_REQUEST_OID = PKCS9_OIDS[14];
204: public static final ObjectIdentifier SMIME_CAPABILITY_OID = PKCS9_OIDS[15];
205:
206: public static final String EMAIL_ADDRESS_STR = "EmailAddress";
207: public static final String UNSTRUCTURED_NAME_STR = "UnstructuredName";
208: public static final String CONTENT_TYPE_STR = "ContentType";
209: public static final String MESSAGE_DIGEST_STR = "MessageDigest";
210: public static final String SIGNING_TIME_STR = "SigningTime";
211: public static final String COUNTERSIGNATURE_STR = "Countersignature";
212: public static final String CHALLENGE_PASSWORD_STR = "ChallengePassword";
213: public static final String UNSTRUCTURED_ADDRESS_STR = "UnstructuredAddress";
214: public static final String EXTENDED_CERTIFICATE_ATTRIBUTES_STR = "ExtendedCertificateAttributes";
215: public static final String ISSUER_SERIALNUMBER_STR = "IssuerAndSerialNumber";
216: // [11], [12] are RSA DSI proprietary
217: private static final String RSA_PROPRIETARY_STR = "RSAProprietary";
218: // [13] ==> signingDescription, S/MIME, not used anymore
219: private static final String SMIME_SIGNING_DESC_STR = "SMIMESigningDesc";
220: public static final String EXTENSION_REQUEST_STR = "ExtensionRequest";
221: public static final String SMIME_CAPABILITY_STR = "SMIMECapability";
222:
223: /**
224: * Hashtable mapping names and variant names of supported
225: * attributes to their OIDs. This table contains all name forms
226: * that occur in PKCS9, in lower case.
227: */
228: private static final Hashtable NAME_OID_TABLE = new Hashtable(16);
229:
230: static { // static initializer for PCKS9_NAMES
231: NAME_OID_TABLE.put("emailaddress", PKCS9_OIDS[1]);
232: NAME_OID_TABLE.put("unstructuredname", PKCS9_OIDS[2]);
233: NAME_OID_TABLE.put("contenttype", PKCS9_OIDS[3]);
234: NAME_OID_TABLE.put("messagedigest", PKCS9_OIDS[4]);
235: NAME_OID_TABLE.put("signingtime", PKCS9_OIDS[5]);
236: NAME_OID_TABLE.put("countersignature", PKCS9_OIDS[6]);
237: NAME_OID_TABLE.put("challengepassword", PKCS9_OIDS[7]);
238: NAME_OID_TABLE.put("unstructuredaddress", PKCS9_OIDS[8]);
239: NAME_OID_TABLE.put("extendedcertificateattributes",
240: PKCS9_OIDS[9]);
241: NAME_OID_TABLE.put("issuerandserialnumber", PKCS9_OIDS[10]);
242: NAME_OID_TABLE.put("rsaproprietary", PKCS9_OIDS[11]);
243: NAME_OID_TABLE.put("rsaproprietary", PKCS9_OIDS[12]);
244: NAME_OID_TABLE.put("signingdescription", PKCS9_OIDS[13]);
245: NAME_OID_TABLE.put("extensionrequest", PKCS9_OIDS[14]);
246: NAME_OID_TABLE.put("smimecapability", PKCS9_OIDS[15]);
247: };
248:
249: /**
250: * Hashtable mapping attribute OIDs defined in PKCS9 to the
251: * corresponding attribute value type.
252: */
253: private static final Hashtable OID_NAME_TABLE = new Hashtable(16);
254: static {
255: OID_NAME_TABLE.put(PKCS9_OIDS[1], EMAIL_ADDRESS_STR);
256: OID_NAME_TABLE.put(PKCS9_OIDS[2], UNSTRUCTURED_NAME_STR);
257: OID_NAME_TABLE.put(PKCS9_OIDS[3], CONTENT_TYPE_STR);
258: OID_NAME_TABLE.put(PKCS9_OIDS[4], MESSAGE_DIGEST_STR);
259: OID_NAME_TABLE.put(PKCS9_OIDS[5], SIGNING_TIME_STR);
260: OID_NAME_TABLE.put(PKCS9_OIDS[6], COUNTERSIGNATURE_STR);
261: OID_NAME_TABLE.put(PKCS9_OIDS[7], CHALLENGE_PASSWORD_STR);
262: OID_NAME_TABLE.put(PKCS9_OIDS[8], UNSTRUCTURED_ADDRESS_STR);
263: OID_NAME_TABLE.put(PKCS9_OIDS[9],
264: EXTENDED_CERTIFICATE_ATTRIBUTES_STR);
265: OID_NAME_TABLE.put(PKCS9_OIDS[10], ISSUER_SERIALNUMBER_STR);
266: OID_NAME_TABLE.put(PKCS9_OIDS[11], RSA_PROPRIETARY_STR);
267: OID_NAME_TABLE.put(PKCS9_OIDS[12], RSA_PROPRIETARY_STR);
268: OID_NAME_TABLE.put(PKCS9_OIDS[13], SMIME_SIGNING_DESC_STR);
269: OID_NAME_TABLE.put(PKCS9_OIDS[14], EXTENSION_REQUEST_STR);
270: OID_NAME_TABLE.put(PKCS9_OIDS[15], SMIME_CAPABILITY_STR);
271: }
272:
273: /**
274: * Acceptable ASN.1 tags for DER encodings of values of PKCS9
275: * attributes, by index in <code>PKCS9_OIDS</code>.
276: * Sets of acceptable tags are represented as arrays.
277: */
278: private static final Byte[][] PKCS9_VALUE_TAGS = {
279: null,
280: { new Byte(DerValue.tag_IA5String) }, // EMailAddress
281: { new Byte(DerValue.tag_IA5String) }, // UnstructuredName
282: { new Byte(DerValue.tag_ObjectId) }, // ContentType
283: { new Byte(DerValue.tag_OctetString) }, // MessageDigest
284: { new Byte(DerValue.tag_UtcTime) }, // SigningTime
285: { new Byte(DerValue.tag_Sequence) }, // Countersignature
286: { new Byte(DerValue.tag_PrintableString),
287: new Byte(DerValue.tag_T61String) }, // ChallengePassword
288: { new Byte(DerValue.tag_PrintableString),
289: new Byte(DerValue.tag_T61String) }, // UnstructuredAddress
290: { new Byte(DerValue.tag_SetOf) }, // ExtendedCertificateAttributes
291: { new Byte(DerValue.tag_Sequence) }, // issuerAndSerialNumber
292: null, null, null, { new Byte(DerValue.tag_Sequence) }, // extensionRequest
293: { new Byte(DerValue.tag_Sequence) } // SMIMECapability
294: };
295:
296: private static final Class[] VALUE_CLASSES = new Class[16];
297:
298: static {
299: try {
300: Class str = Class.forName("[Ljava.lang.String;");
301:
302: VALUE_CLASSES[0] = null; // not used
303: VALUE_CLASSES[1] = str; // EMailAddress
304: VALUE_CLASSES[2] = str; // UnstructuredName
305: VALUE_CLASSES[3] = // ContentType
306: Class.forName("sun.security.util.ObjectIdentifier");
307: VALUE_CLASSES[4] = Class.forName("[B"); // MessageDigest (byte[])
308: VALUE_CLASSES[5] = Class.forName("java.util.Date"); // SigningTime
309: VALUE_CLASSES[6] = // Countersignature
310: Class.forName("[Lsun.security.pkcs.SignerInfo;");
311: VALUE_CLASSES[7] = // ChallengePassword
312: Class.forName("java.lang.String");
313: VALUE_CLASSES[8] = str; // UnstructuredAddress
314: VALUE_CLASSES[9] = null; // ExtendedCertificateAttributes
315: VALUE_CLASSES[10] = null; // IssuerAndSerialNumber
316: VALUE_CLASSES[11] = null; // not used
317: VALUE_CLASSES[12] = null; // not used
318: VALUE_CLASSES[13] = null; // not used
319: VALUE_CLASSES[14] = // ExtensionRequest
320: Class.forName("sun.security.x509.CertificateExtensions");
321: VALUE_CLASSES[15] = null; // not supported yet
322: } catch (ClassNotFoundException e) {
323: throw new ExceptionInInitializerError(e.toString());
324: }
325: }
326:
327: /**
328: * Array indicating which PKCS9 attributes are single-valued,
329: * by index in <code>PKCS9_OIDS</code>.
330: */
331: private static final boolean[] SINGLE_VALUED = { false, false, // EMailAddress
332: false, // UnstructuredName
333: true, // ContentType
334: true, // MessageDigest
335: true, // SigningTime
336: false, // Countersignature
337: true, // ChallengePassword
338: false, // UnstructuredAddress
339: false, // ExtendedCertificateAttributes
340: true, // IssuerAndSerialNumber - not supported yet
341: false, // not used
342: false, // not used
343: false, // not used
344: true, // ExtensionRequest
345: true // SMIMECapability - not supported yet
346: };
347:
348: /**
349: * The OID of this attribute is <code>PKCS9_OIDS[index]</code>.
350: */
351: private int index;
352:
353: /**
354: * Value set of this attribute. Its class is given by
355: * <code>VALUE_CLASSES[index]</code>.
356: */
357: private Object value;
358:
359: /**
360: * Construct an attribute object from the attribute's OID and
361: * value. If the attribute is single-valued, provide only one
362: * value. If the attribute is multi-valued, provide an array
363: * containing all the values.
364: * Arrays of length zero are accepted, though probably useless.
365: *
366: * <P> The
367: * <a href=#classTable>table</a> gives the class that <code>value</code>
368: * must have for a given attribute.
369: *
370: */
371: public PKCS9Attribute(ObjectIdentifier oid, Object value)
372: throws IllegalArgumentException {
373: init(oid, value);
374: }
375:
376: /**
377: * Construct an attribute object from the attribute's name and
378: * value. If the attribute is single-valued, provide only one
379: * value. If the attribute is multi-valued, provide an array
380: * containing all the values.
381: * Arrays of length zero are accepted, though probably useless.
382: *
383: * <P> The
384: * <a href=#classTable>table</a> gives the class that <code>value</code>
385: * must have for a given attribute. Reasonable variants of these
386: * attributes are accepted; in particular, case does not matter.
387: *
388: * @exception IllegalArgumentException
389: * if the <code>name</code> is not recognized of the
390: * <code>value</code> has the wrong type.
391: */
392: public PKCS9Attribute(String name, Object value)
393: throws IllegalArgumentException {
394: ObjectIdentifier oid = getOID(name);
395:
396: if (oid == null)
397: throw new IllegalArgumentException(
398: "Unrecognized attribute name " + name
399: + " constructing PKCS9Attribute.");
400:
401: init(oid, value);
402: }
403:
404: private void init(ObjectIdentifier oid, Object value)
405: throws IllegalArgumentException {
406:
407: index = indexOf(oid, PKCS9_OIDS, 1);
408:
409: if (index == -1)
410: throw new IllegalArgumentException("Unsupported OID " + oid
411: + " constructing PKCS9Attribute.");
412:
413: if (!VALUE_CLASSES[index].isInstance(value))
414: throw new IllegalArgumentException("Wrong value class "
415: + " for attribute " + oid
416: + " constructing PKCS9Attribute; was "
417: + value.getClass().toString() + ", should be "
418: + VALUE_CLASSES[index].toString());
419:
420: this .value = value;
421: }
422:
423: /**
424: * Construct a PKCS9Attribute from its encoding on an input
425: * stream.
426: *
427: * @param val the DerValue representing the DER encoding of the attribute.
428: * @exception IOException on parsing error.
429: */
430: public PKCS9Attribute(DerValue derVal) throws IOException {
431:
432: DerInputStream derIn = new DerInputStream(derVal.toByteArray());
433: DerValue[] val = derIn.getSequence(2);
434:
435: if (derIn.available() != 0)
436: throw new IOException("Excess data parsing PKCS9Attribute");
437:
438: if (val.length != 2)
439: throw new IOException(
440: "PKCS9Attribute doesn't have two components");
441:
442: // get the oid
443: ObjectIdentifier oid = val[0].getOID();
444: index = indexOf(oid, PKCS9_OIDS, 1);
445: if (index == -1)
446: throw new IOException("Invalid OID for PKCS9 attribute: "
447: + oid);
448:
449: DerValue[] elems = new DerInputStream(val[1].toByteArray())
450: .getSet(1);
451:
452: // check single valued have only one value
453: if (SINGLE_VALUED[index] && elems.length > 1)
454: throwSingleValuedException();
455:
456: // check for illegal element tags
457: Byte tag;
458: for (int i = 0; i < elems.length; i++) {
459: tag = new Byte(elems[i].tag);
460:
461: if (indexOf(tag, PKCS9_VALUE_TAGS[index], 0) == -1)
462: throwTagException(tag);
463: }
464:
465: switch (index) {
466: case 1: // email address
467: case 2: // unstructured name
468: case 8: // unstructured address
469: { // open scope
470: String[] values = new String[elems.length];
471:
472: for (int i = 0; i < elems.length; i++)
473: values[i] = elems[i].getAsString();
474: value = values;
475: } // close scope
476: break;
477:
478: case 3: // content type
479: value = elems[0].getOID();
480: break;
481:
482: case 4: // message digest
483: value = elems[0].getOctetString();
484: break;
485:
486: case 5: // signing time
487: value = (new DerInputStream(elems[0].toByteArray()))
488: .getUTCTime();
489: break;
490:
491: case 6: // countersignature
492: { // open scope
493: SignerInfo[] values = new SignerInfo[elems.length];
494: for (int i = 0; i < elems.length; i++)
495: values[i] = new SignerInfo(elems[i].toDerInputStream());
496: value = values;
497: } // close scope
498: break;
499:
500: case 7: // challenge password
501: value = elems[0].getAsString();
502: break;
503:
504: case 9: // extended-certificate attribute -- not supported
505: throw new IOException("PKCS9 extended-certificate "
506: + "attribute not supported.");
507: // break unnecessary
508: case 10: // issuerAndserialNumber attribute -- not supported
509: throw new IOException("PKCS9 IssuerAndSerialNumber"
510: + "attribute not supported.");
511: // break unnecessary
512: case 11: // RSA DSI proprietary
513: case 12: // RSA DSI proprietary
514: throw new IOException("PKCS9 RSA DSI attributes"
515: + "11 and 12, not supported.");
516: // break unnecessary
517: case 13: // S/MIME unused attribute
518: throw new IOException("PKCS9 attribute #13 not supported.");
519: // break unnecessary
520:
521: case 14: // ExtensionRequest
522: value = new CertificateExtensions(new DerInputStream(
523: elems[0].toByteArray()));
524: break;
525:
526: case 15: // SMIME-capability attribute -- not supported
527: throw new IOException("PKCS9 SMIMECapability "
528: + "attribute not supported.");
529: // break unnecessary
530: default: // can't happen
531: }
532: }
533:
534: /**
535: * Write the DER encoding of this attribute to an output stream.
536: *
537: * <P> N.B.: This method always encodes values of
538: * ChallengePassword and UnstructuredAddress attributes as ASN.1
539: * <code>PrintableString</code>s, without checking whether they
540: * should be encoded as <code>T61String</code>s.
541: */
542: public void derEncode(OutputStream out) throws IOException {
543: DerOutputStream temp = new DerOutputStream();
544: temp.putOID(getOID());
545: switch (index) {
546: case 1: // email address
547: case 2: // unstructured name
548: { // open scope
549: String[] values = (String[]) value;
550: DerOutputStream[] temps = new DerOutputStream[values.length];
551:
552: for (int i = 0; i < values.length; i++) {
553: temps[i] = new DerOutputStream();
554: temps[i].putIA5String(values[i]);
555: }
556: temp.putOrderedSetOf(DerValue.tag_Set, temps);
557: } // close scope
558: break;
559:
560: case 3: // content type
561: {
562: DerOutputStream temp2 = new DerOutputStream();
563: temp2.putOID((ObjectIdentifier) value);
564: temp.write(DerValue.tag_Set, temp2.toByteArray());
565: }
566: break;
567:
568: case 4: // message digest
569: {
570: DerOutputStream temp2 = new DerOutputStream();
571: temp2.putOctetString((byte[]) value);
572: temp.write(DerValue.tag_Set, temp2.toByteArray());
573: }
574: break;
575:
576: case 5: // signing time
577: {
578: DerOutputStream temp2 = new DerOutputStream();
579: temp2.putUTCTime((Date) value);
580: temp.write(DerValue.tag_Set, temp2.toByteArray());
581: }
582: break;
583:
584: case 6: // countersignature
585: temp
586: .putOrderedSetOf(DerValue.tag_Set,
587: (DerEncoder[]) value);
588: break;
589:
590: case 7: // challenge password
591: {
592: DerOutputStream temp2 = new DerOutputStream();
593: temp2.putPrintableString((String) value);
594: temp.write(DerValue.tag_Set, temp2.toByteArray());
595: }
596: break;
597:
598: case 8: // unstructured address
599: { // open scope
600: String[] values = (String[]) value;
601: DerOutputStream[] temps = new DerOutputStream[values.length];
602:
603: for (int i = 0; i < values.length; i++) {
604: temps[i] = new DerOutputStream();
605: temps[i].putPrintableString(values[i]);
606: }
607: temp.putOrderedSetOf(DerValue.tag_Set, temps);
608: } // close scope
609: break;
610:
611: case 9: // extended-certificate attribute -- not supported
612: throw new IOException("PKCS9 extended-certificate "
613: + "attribute not supported.");
614: // break unnecessary
615: case 10: // issuerAndserialNumber attribute -- not supported
616: throw new IOException("PKCS9 IssuerAndSerialNumber"
617: + "attribute not supported.");
618: // break unnecessary
619: case 11: // RSA DSI proprietary
620: case 12: // RSA DSI proprietary
621: throw new IOException("PKCS9 RSA DSI attributes"
622: + "11 and 12, not supported.");
623: // break unnecessary
624: case 13: // S/MIME unused attribute
625: throw new IOException("PKCS9 attribute #13 not supported.");
626: // break unnecessary
627:
628: case 14: // ExtensionRequest
629: {
630: DerOutputStream temp2 = new DerOutputStream();
631: CertificateExtensions exts = (CertificateExtensions) value;
632: try {
633: exts.encode(temp2, true);
634: } catch (CertificateException ex) {
635: throw new IOException(ex.toString());
636: }
637: temp.write(DerValue.tag_Set, temp2.toByteArray());
638: }
639: break;
640: case 15: // SMIMECapability
641: throw new IOException("PKCS9 attribute #15 not supported.");
642: // break unnecessary
643: default: // can't happen
644: }
645:
646: DerOutputStream derOut = new DerOutputStream();
647: derOut.write(DerValue.tag_Sequence, temp.toByteArray());
648:
649: out.write(derOut.toByteArray());
650:
651: }
652:
653: /**
654: * Get the value of this attribute. If the attribute is
655: * single-valued, return just the one value. If the attribute is
656: * multi-valued, return an array containing all the values.
657: * It is possible for this array to be of length 0.
658: *
659: * <P> The
660: * <a href=#classTable>table</a> gives the class of the value returned,
661: * depending on the type of this attribute.
662: */
663: public Object getValue() {
664: return value;
665: }
666:
667: /**
668: * Show whether this attribute is single-valued.
669: */
670: public boolean isSingleValued() {
671: return SINGLE_VALUED[index];
672: }
673:
674: /**
675: * Return the OID of this attribute.
676: */
677: public ObjectIdentifier getOID() {
678: return PKCS9_OIDS[index];
679: }
680:
681: /**
682: * Return the name of this attribute.
683: */
684: public String getName() {
685: return (String) OID_NAME_TABLE.get(PKCS9_OIDS[index]);
686: }
687:
688: /**
689: * Return the OID for a given attribute name or null if we don't recognize
690: * the name.
691: */
692: public static ObjectIdentifier getOID(String name) {
693: return (ObjectIdentifier) NAME_OID_TABLE
694: .get(name.toLowerCase());
695: }
696:
697: /**
698: * Return the attribute name for a given OID or null if we don't recognize
699: * the oid.
700: */
701: public static String getName(ObjectIdentifier oid) {
702: return (String) OID_NAME_TABLE.get(oid);
703: }
704:
705: /**
706: * Returns a string representation of this attribute.
707: */
708: public String toString() {
709: StringBuffer buf = new StringBuffer(100);
710:
711: buf.append("[");
712:
713: buf.append(OID_NAME_TABLE.get(PKCS9_OIDS[index]));
714: buf.append(": ");
715:
716: if (SINGLE_VALUED[index]) {
717: if (value instanceof byte[]) { // special case for octet string
718: HexDumpEncoder hexDump = new HexDumpEncoder();
719: buf.append(hexDump.encodeBuffer((byte[]) value));
720: } else {
721: buf.append(value.toString());
722: }
723: buf.append("]");
724: return buf.toString();
725: } else { // multi-valued
726: boolean first = true;
727: Object[] values = (Object[]) value;
728:
729: for (int j = 0; j < values.length; j++) {
730: if (first)
731: first = false;
732: else
733: buf.append(", ");
734:
735: buf.append(values[j].toString());
736: }
737: return buf.toString();
738: }
739: }
740:
741: /**
742: * Beginning the search at <code>start</code>, find the first
743: * index <code>i</code> such that <code>a[i] = obj</code>.
744: *
745: * @return the index, if found, and -1 otherwise.
746: */
747: static int indexOf(Object obj, Object[] a, int start) {
748: for (int i = start; i < a.length; i++) {
749: if (obj.equals(a[i]))
750: return i;
751: }
752: return -1;
753: }
754:
755: /**
756: * Throw an exception when there are multiple values for
757: * a single-valued attribute.
758: */
759: private void throwSingleValuedException() throws IOException {
760: throw new IOException("Single-value attribute " + getOID()
761: + " (" + getName() + ")" + " has multiple values.");
762: }
763:
764: /**
765: * Throw an exception when the tag on a value encoding is
766: * wrong for the attribute whose value it is.
767: */
768: private void throwTagException(Byte tag) throws IOException {
769: Byte[] expectedTags = PKCS9_VALUE_TAGS[index];
770: StringBuffer msg = new StringBuffer(100);
771: msg.append("Value of attribute ");
772: msg.append(getOID().toString());
773: msg.append(" (");
774: msg.append(getName());
775: msg.append(") has wrong tag: ");
776: msg.append(tag.toString());
777: msg.append(". Expected tags: ");
778:
779: msg.append(expectedTags[0].toString());
780:
781: for (int i = 1; i < expectedTags.length; i++) {
782: msg.append(", ");
783: msg.append(expectedTags[i].toString());
784: }
785: msg.append(".");
786: throw new IOException(msg.toString());
787: }
788: }
|