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