001: /*
002: * Copyright 1996-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.security.util;
027:
028: import java.io.FilterOutputStream;
029: import java.io.ByteArrayOutputStream;
030: import java.io.OutputStream;
031: import java.io.IOException;
032: import java.text.SimpleDateFormat;
033: import java.util.Date;
034: import java.util.TimeZone;
035: import java.util.Vector;
036: import java.util.Comparator;
037: import java.util.Arrays;
038: import java.math.BigInteger;
039:
040: /**
041: * Output stream marshaling DER-encoded data. This is eventually provided
042: * in the form of a byte array; there is no advance limit on the size of
043: * that byte array.
044: *
045: * <P>At this time, this class supports only a subset of the types of
046: * DER data encodings which are defined. That subset is sufficient for
047: * generating most X.509 certificates.
048: *
049: * @version 1.57
050: *
051: * @author David Brownell
052: * @author Amit Kapoor
053: * @author Hemma Prafullchandra
054: */
055: public class DerOutputStream extends ByteArrayOutputStream implements
056: DerEncoder {
057: /**
058: * Construct an DER output stream.
059: *
060: * @param size how large a buffer to preallocate.
061: */
062: public DerOutputStream(int size) {
063: super (size);
064: }
065:
066: /**
067: * Construct an DER output stream.
068: */
069: public DerOutputStream() {
070: }
071:
072: /**
073: * Writes tagged, pre-marshaled data. This calcuates and encodes
074: * the length, so that the output data is the standard triple of
075: * { tag, length, data } used by all DER values.
076: *
077: * @param tag the DER value tag for the data, such as
078: * <em>DerValue.tag_Sequence</em>
079: * @param buf buffered data, which must be DER-encoded
080: */
081: public void write(byte tag, byte[] buf) throws IOException {
082: write(tag);
083: putLength(buf.length);
084: write(buf, 0, buf.length);
085: }
086:
087: /**
088: * Writes tagged data using buffer-to-buffer copy. As above,
089: * this writes a standard DER record. This is often used when
090: * efficiently encapsulating values in sequences.
091: *
092: * @param tag the DER value tag for the data, such as
093: * <em>DerValue.tag_Sequence</em>
094: * @param out buffered data
095: */
096: public void write(byte tag, DerOutputStream out) throws IOException {
097: write(tag);
098: putLength(out.count);
099: write(out.buf, 0, out.count);
100: }
101:
102: /**
103: * Writes implicitly tagged data using buffer-to-buffer copy. As above,
104: * this writes a standard DER record. This is often used when
105: * efficiently encapsulating implicitly tagged values.
106: *
107: * @param tag the DER value of the context-specific tag that replaces
108: * original tag of the value in the output, such as in
109: * <pre>
110: * <em> <field> [N] IMPLICIT <type></em>
111: * </pre>
112: * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, with value=4;
113: * would be encoded as "81 01 04" whereas in explicit
114: * tagging it would be encoded as "A1 03 02 01 04".
115: * Notice that the tag is A1 and not 81, this is because with
116: * explicit tagging the form is always constructed.
117: * @param value original value being implicitly tagged
118: */
119: public void writeImplicit(byte tag, DerOutputStream value)
120: throws IOException {
121: write(tag);
122: write(value.buf, 1, value.count - 1);
123: }
124:
125: /**
126: * Marshals pre-encoded DER value onto the output stream.
127: */
128: public void putDerValue(DerValue val) throws IOException {
129: val.encode(this );
130: }
131:
132: /*
133: * PRIMITIVES -- these are "universal" ASN.1 simple types.
134: *
135: * BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL
136: * OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF)
137: * PrintableString, T61String, IA5String, UTCTime
138: */
139:
140: /**
141: * Marshals a DER boolean on the output stream.
142: */
143: public void putBoolean(boolean val) throws IOException {
144: write(DerValue.tag_Boolean);
145: putLength(1);
146: if (val) {
147: write(0xff);
148: } else {
149: write(0);
150: }
151: }
152:
153: /**
154: * Marshals a DER enumerated on the output stream.
155: * @param i the enumerated value.
156: */
157: public void putEnumerated(int i) throws IOException {
158: write(DerValue.tag_Enumerated);
159: putIntegerContents(i);
160: }
161:
162: /**
163: * Marshals a DER integer on the output stream.
164: *
165: * @param i the integer in the form of a BigInteger.
166: */
167: public void putInteger(BigInteger i) throws IOException {
168: write(DerValue.tag_Integer);
169: byte[] buf = i.toByteArray(); // least number of bytes
170: putLength(buf.length);
171: write(buf, 0, buf.length);
172: }
173:
174: /**
175: * Marshals a DER integer on the output stream.
176: * @param i the integer in the form of an Integer.
177: */
178: public void putInteger(Integer i) throws IOException {
179: putInteger(i.intValue());
180: }
181:
182: /**
183: * Marshals a DER integer on the output stream.
184: * @param i the integer.
185: */
186: public void putInteger(int i) throws IOException {
187: write(DerValue.tag_Integer);
188: putIntegerContents(i);
189: }
190:
191: private void putIntegerContents(int i) throws IOException {
192:
193: byte[] bytes = new byte[4];
194: int start = 0;
195:
196: // Obtain the four bytes of the int
197:
198: bytes[3] = (byte) (i & 0xff);
199: bytes[2] = (byte) ((i & 0xff00) >>> 8);
200: bytes[1] = (byte) ((i & 0xff0000) >>> 16);
201: bytes[0] = (byte) ((i & 0xff000000) >>> 24);
202:
203: // Reduce them to the least number of bytes needed to
204: // represent this int
205:
206: if (bytes[0] == 0xff) {
207:
208: // Eliminate redundant 0xff
209:
210: for (int j = 0; j < 3; j++) {
211: if ((bytes[j] == 0xff)
212: && ((bytes[j + 1] & 0x80) == 0x80))
213: start++;
214: else
215: break;
216: }
217: } else if (bytes[0] == 0x00) {
218:
219: // Eliminate redundant 0x00
220:
221: for (int j = 0; j < 3; j++) {
222: if ((bytes[j] == 0x00) && ((bytes[j + 1] & 0x80) == 0))
223: start++;
224: else
225: break;
226: }
227: }
228:
229: putLength(4 - start);
230: for (int k = start; k < 4; k++)
231: write(bytes[k]);
232: }
233:
234: /**
235: * Marshals a DER bit string on the output stream. The bit
236: * string must be byte-aligned.
237: *
238: * @param bits the bit string, MSB first
239: */
240: public void putBitString(byte[] bits) throws IOException {
241: write(DerValue.tag_BitString);
242: putLength(bits.length + 1);
243: write(0); // all of last octet is used
244: write(bits);
245: }
246:
247: /**
248: * Marshals a DER bit string on the output stream.
249: * The bit strings need not be byte-aligned.
250: *
251: * @param bits the bit string, MSB first
252: */
253: public void putUnalignedBitString(BitArray ba) throws IOException {
254: byte[] bits = ba.toByteArray();
255:
256: write(DerValue.tag_BitString);
257: putLength(bits.length + 1);
258: write(bits.length * 8 - ba.length()); // excess bits in last octet
259: write(bits);
260: }
261:
262: /**
263: * Marshals a truncated DER bit string on the output stream.
264: * The bit strings need not be byte-aligned.
265: *
266: * @param bits the bit string, MSB first
267: */
268: public void putTruncatedUnalignedBitString(BitArray ba)
269: throws IOException {
270: putUnalignedBitString(ba.truncate());
271: }
272:
273: /**
274: * DER-encodes an ASN.1 OCTET STRING value on the output stream.
275: *
276: * @param octets the octet string
277: */
278: public void putOctetString(byte[] octets) throws IOException {
279: write(DerValue.tag_OctetString, octets);
280: }
281:
282: /**
283: * Marshals a DER "null" value on the output stream. These are
284: * often used to indicate optional values which have been omitted.
285: */
286: public void putNull() throws IOException {
287: write(DerValue.tag_Null);
288: putLength(0);
289: }
290:
291: /**
292: * Marshals an object identifier (OID) on the output stream.
293: * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct.
294: */
295: public void putOID(ObjectIdentifier oid) throws IOException {
296: oid.encode(this );
297: }
298:
299: /**
300: * Marshals a sequence on the output stream. This supports both
301: * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF"
302: * (one to N values) constructs.
303: */
304: public void putSequence(DerValue[] seq) throws IOException {
305: DerOutputStream bytes = new DerOutputStream();
306: int i;
307:
308: for (i = 0; i < seq.length; i++)
309: seq[i].encode(bytes);
310:
311: write(DerValue.tag_Sequence, bytes);
312: }
313:
314: /**
315: * Marshals the contents of a set on the output stream without
316: * ordering the elements. Ok for BER encoding, but not for DER
317: * encoding.
318: *
319: * For DER encoding, use orderedPutSet() or orderedPutSetOf().
320: */
321: public void putSet(DerValue[] set) throws IOException {
322: DerOutputStream bytes = new DerOutputStream();
323: int i;
324:
325: for (i = 0; i < set.length; i++)
326: set[i].encode(bytes);
327:
328: write(DerValue.tag_Set, bytes);
329: }
330:
331: /**
332: * Marshals the contents of a set on the output stream. Sets
333: * are semantically unordered, but DER requires that encodings of
334: * set elements be sorted into ascending lexicographical order
335: * before being output. Hence sets with the same tags and
336: * elements have the same DER encoding.
337: *
338: * This method supports the ASN.1 "SET OF" construct, but not
339: * "SET", which uses a different order.
340: */
341: public void putOrderedSetOf(byte tag, DerEncoder[] set)
342: throws IOException {
343: putOrderedSet(tag, set, lexOrder);
344: }
345:
346: /**
347: * Marshals the contents of a set on the output stream. Sets
348: * are semantically unordered, but DER requires that encodings of
349: * set elements be sorted into ascending tag order
350: * before being output. Hence sets with the same tags and
351: * elements have the same DER encoding.
352: *
353: * This method supports the ASN.1 "SET" construct, but not
354: * "SET OF", which uses a different order.
355: */
356: public void putOrderedSet(byte tag, DerEncoder[] set)
357: throws IOException {
358: putOrderedSet(tag, set, tagOrder);
359: }
360:
361: /**
362: * Lexicographical order comparison on byte arrays, for ordering
363: * elements of a SET OF objects in DER encoding.
364: */
365: private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder();
366:
367: /**
368: * Tag order comparison on byte arrays, for ordering elements of
369: * SET objects in DER encoding.
370: */
371: private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder();
372:
373: /**
374: * Marshals a the contents of a set on the output stream with the
375: * encodings of its sorted in increasing order.
376: *
377: * @param order the order to use when sorting encodings of components.
378: */
379: private void putOrderedSet(byte tag, DerEncoder[] set,
380: Comparator<byte[]> order) throws IOException {
381: DerOutputStream[] streams = new DerOutputStream[set.length];
382:
383: for (int i = 0; i < set.length; i++) {
384: streams[i] = new DerOutputStream();
385: set[i].derEncode(streams[i]);
386: }
387:
388: // order the element encodings
389: byte[][] bufs = new byte[streams.length][];
390: for (int i = 0; i < streams.length; i++) {
391: bufs[i] = streams[i].toByteArray();
392: }
393: Arrays.<byte[]> sort(bufs, order);
394:
395: DerOutputStream bytes = new DerOutputStream();
396: for (int i = 0; i < streams.length; i++) {
397: bytes.write(bufs[i]);
398: }
399: write(tag, bytes);
400:
401: }
402:
403: /**
404: * Marshals a string as a DER encoded UTF8String.
405: */
406: public void putUTF8String(String s) throws IOException {
407: writeString(s, DerValue.tag_UTF8String, "UTF8");
408: }
409:
410: /**
411: * Marshals a string as a DER encoded PrintableString.
412: */
413: public void putPrintableString(String s) throws IOException {
414: writeString(s, DerValue.tag_PrintableString, "ASCII");
415: }
416:
417: /**
418: * Marshals a string as a DER encoded T61String.
419: */
420: public void putT61String(String s) throws IOException {
421: /*
422: * Works for characters that are defined in both ASCII and
423: * T61.
424: */
425: writeString(s, DerValue.tag_T61String, "ISO-8859-1");
426: }
427:
428: /**
429: * Marshals a string as a DER encoded IA5String.
430: */
431: public void putIA5String(String s) throws IOException {
432: writeString(s, DerValue.tag_IA5String, "ASCII");
433: }
434:
435: /**
436: * Marshals a string as a DER encoded BMPString.
437: */
438: public void putBMPString(String s) throws IOException {
439: writeString(s, DerValue.tag_BMPString, "UnicodeBigUnmarked");
440: }
441:
442: /**
443: * Marshals a string as a DER encoded GeneralString.
444: */
445: public void putGeneralString(String s) throws IOException {
446: writeString(s, DerValue.tag_GeneralString, "ASCII");
447: }
448:
449: /**
450: * Private helper routine for writing DER encoded string values.
451: * @param s the string to write
452: * @param stringTag one of the DER string tags that indicate which
453: * encoding should be used to write the string out.
454: * @param enc the name of the encoder that should be used corresponding
455: * to the above tag.
456: */
457: private void writeString(String s, byte stringTag, String enc)
458: throws IOException {
459:
460: byte[] data = s.getBytes(enc);
461: write(stringTag);
462: putLength(data.length);
463: write(data);
464: }
465:
466: /**
467: * Marshals a DER UTC time/date value.
468: *
469: * <P>YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time
470: * and with seconds (even if seconds=0) as per RFC 3280.
471: */
472: public void putUTCTime(Date d) throws IOException {
473: putTime(d, DerValue.tag_UtcTime);
474: }
475:
476: /**
477: * Marshals a DER Generalized Time/date value.
478: *
479: * <P>YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time
480: * and with seconds (even if seconds=0) as per RFC 3280.
481: */
482: public void putGeneralizedTime(Date d) throws IOException {
483: putTime(d, DerValue.tag_GeneralizedTime);
484: }
485:
486: /**
487: * Private helper routine for marshalling a DER UTC/Generalized
488: * time/date value. If the tag specified is not that for UTC Time
489: * then it defaults to Generalized Time.
490: * @param d the date to be marshalled
491: * @param tag the tag for UTC Time or Generalized Time
492: */
493: private void putTime(Date d, byte tag) throws IOException {
494:
495: /*
496: * Format the date.
497: */
498:
499: TimeZone tz = TimeZone.getTimeZone("GMT");
500: String pattern = null;
501:
502: if (tag == DerValue.tag_UtcTime) {
503: pattern = "yyMMddHHmmss'Z'";
504: } else {
505: tag = DerValue.tag_GeneralizedTime;
506: pattern = "yyyyMMddHHmmss'Z'";
507: }
508:
509: SimpleDateFormat sdf = new SimpleDateFormat(pattern);
510: sdf.setTimeZone(tz);
511: byte[] time = (sdf.format(d)).getBytes("ISO-8859-1");
512:
513: /*
514: * Write the formatted date.
515: */
516:
517: write(tag);
518: putLength(time.length);
519: write(time);
520: }
521:
522: /**
523: * Put the encoding of the length in the stream.
524: *
525: * @params len the length of the attribute.
526: * @exception IOException on writing errors.
527: */
528: public void putLength(int len) throws IOException {
529: if (len < 128) {
530: write((byte) len);
531:
532: } else if (len < (1 << 8)) {
533: write((byte) 0x081);
534: write((byte) len);
535:
536: } else if (len < (1 << 16)) {
537: write((byte) 0x082);
538: write((byte) (len >> 8));
539: write((byte) len);
540:
541: } else if (len < (1 << 24)) {
542: write((byte) 0x083);
543: write((byte) (len >> 16));
544: write((byte) (len >> 8));
545: write((byte) len);
546:
547: } else {
548: write((byte) 0x084);
549: write((byte) (len >> 24));
550: write((byte) (len >> 16));
551: write((byte) (len >> 8));
552: write((byte) len);
553: }
554: }
555:
556: /**
557: * Put the tag of the attribute in the stream.
558: *
559: * @params class the tag class type, one of UNIVERSAL, CONTEXT,
560: * APPLICATION or PRIVATE
561: * @params form if true, the value is constructed, otherwise it is
562: * primitive.
563: * @params val the tag value
564: */
565: public void putTag(byte tagClass, boolean form, byte val) {
566: byte tag = (byte) (tagClass | val);
567: if (form) {
568: tag |= (byte) 0x20;
569: }
570: write(tag);
571: }
572:
573: /**
574: * Write the current contents of this <code>DerOutputStream</code>
575: * to an <code>OutputStream</code>.
576: *
577: * @exception IOException on output error.
578: */
579: public void derEncode(OutputStream out) throws IOException {
580: out.write(toByteArray());
581: }
582: }
|