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