576 lines · 17.5 KB · Java
RawDownload
/* * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.util; import java.io.ByteArrayOutputStream;import java.io.OutputStream;import java.io.IOException;import java.text.SimpleDateFormat;import java.util.Date;import java.util.TimeZone;import java.util.Comparator;import java.util.Arrays;import java.math.BigInteger;import java.util.Locale;  /** * Output stream marshaling DER-encoded data.  This is eventually provided * in the form of a byte array; there is no advance limit on the size of * that byte array. * * <P>At this time, this class supports only a subset of the types of * DER data encodings which are defined.  That subset is sufficient for * generating most X.509 certificates. * * * @author David Brownell * @author Amit Kapoor * @author Hemma Prafullchandra */public class DerOutputStreamextends ByteArrayOutputStream implements DerEncoder {    /**     * Construct an DER output stream.     *     * @param size how large a buffer to preallocate.     */    public DerOutputStream(int size) { super(size); }     /**     * Construct an DER output stream.     */    public DerOutputStream() { }     /**     * Writes tagged, pre-marshaled data.  This calcuates and encodes     * the length, so that the output data is the standard triple of     * { tag, length, data } used by all DER values.     *     * @param tag the DER value tag for the data, such as     *          <em>DerValue.tag_Sequence</em>     * @param buf buffered data, which must be DER-encoded     */    public void write(byte tag, byte[] buf) throws IOException {        write(tag);        putLength(buf.length);        write(buf, 0, buf.length);    }     /**     * Writes tagged data using buffer-to-buffer copy.  As above,     * this writes a standard DER record.  This is often used when     * efficiently encapsulating values in sequences.     *     * @param tag the DER value tag for the data, such as     *          <em>DerValue.tag_Sequence</em>     * @param out buffered data     */    public void write(byte tag, DerOutputStream out) throws IOException {        write(tag);        putLength(out.count);        write(out.buf, 0, out.count);    }     /**     * Writes implicitly tagged data using buffer-to-buffer copy.  As above,     * this writes a standard DER record.  This is often used when     * efficiently encapsulating implicitly tagged values.     *     * @param tag the DER value of the context-specific tag that replaces     * original tag of the value in the output, such as in     * <pre>     *          <em> <field> [N] IMPLICIT <type></em>     * </pre>     * For example, <em>FooLength [1] IMPLICIT INTEGER</em>, with value=4;     * would be encoded as "81 01 04"  whereas in explicit     * tagging it would be encoded as "A1 03 02 01 04".     * Notice that the tag is A1 and not 81, this is because with     * explicit tagging the form is always constructed.     * @param value original value being implicitly tagged     */    public void writeImplicit(byte tag, DerOutputStream value)    throws IOException {        write(tag);        write(value.buf, 1, value.count-1);    }     /**     * Marshals pre-encoded DER value onto the output stream.     */    public void putDerValue(DerValue val) throws IOException {        val.encode(this);    }     /*     * PRIMITIVES -- these are "universal" ASN.1 simple types.     *     *  BOOLEAN, INTEGER, BIT STRING, OCTET STRING, NULL     *  OBJECT IDENTIFIER, SEQUENCE(OF), SET(OF)     *  PrintableString, T61String, IA5String, UTCTime     */     /**     * Marshals a DER boolean on the output stream.     */    public void putBoolean(boolean val) throws IOException {        write(DerValue.tag_Boolean);        putLength(1);        if (val) {            write(0xff);        } else {            write(0);        }    }     /**     * Marshals a DER enumerated on the output stream.     * @param i the enumerated value.     */    public void putEnumerated(int i) throws IOException {        write(DerValue.tag_Enumerated);        putIntegerContents(i);    }     /**     * Marshals a DER integer on the output stream.     *     * @param i the integer in the form of a BigInteger.     */    public void putInteger(BigInteger i) throws IOException {        write(DerValue.tag_Integer);        byte[]    buf = i.toByteArray(); // least number  of bytes        putLength(buf.length);        write(buf, 0, buf.length);    }     /**     * Marshals a DER integer on the output stream.     * @param i the integer in the form of an Integer.     */    public void putInteger(Integer i) throws IOException {        putInteger(i.intValue());    }     /**     * Marshals a DER integer on the output stream.     * @param i the integer.     */    public void putInteger(int i) throws IOException {        write(DerValue.tag_Integer);        putIntegerContents(i);    }     private void putIntegerContents(int i) throws IOException {         byte[] bytes = new byte[4];        int start = 0;         // Obtain the four bytes of the int         bytes[3] = (byte) (i & 0xff);        bytes[2] = (byte)((i & 0xff00) >>> 8);        bytes[1] = (byte)((i & 0xff0000) >>> 16);        bytes[0] = (byte)((i & 0xff000000) >>> 24);         // Reduce them to the least number of bytes needed to        // represent this int         if (bytes[0] == (byte)0xff) {             // Eliminate redundant 0xff             for (int j = 0; j < 3; j++) {                if ((bytes[j] == (byte)0xff) &&                    ((bytes[j+1] & 0x80) == 0x80))                    start++;                else                    break;             }         } else if (bytes[0] == 0x00) {              // Eliminate redundant 0x00             for (int j = 0; j < 3; j++) {                if ((bytes[j] == 0x00) &&                    ((bytes[j+1] & 0x80) == 0))                    start++;                else                    break;            }        }         putLength(4 - start);        for (int k = start; k < 4; k++)            write(bytes[k]);    }     /**     * Marshals a DER bit string on the output stream. The bit     * string must be byte-aligned.     *     * @param bits the bit string, MSB first     */    public void putBitString(byte[] bits) throws IOException {        write(DerValue.tag_BitString);        putLength(bits.length + 1);        write(0);               // all of last octet is used        write(bits);    }     /**     * Marshals a DER bit string on the output stream.     * The bit strings need not be byte-aligned.     *     * @param bits the bit string, MSB first     */    public void putUnalignedBitString(BitArray ba) throws IOException {        byte[] bits = ba.toByteArray();         write(DerValue.tag_BitString);        putLength(bits.length + 1);        write(bits.length*8 - ba.length()); // excess bits in last octet        write(bits);    }     /**     * Marshals a truncated DER bit string on the output stream.     * The bit strings need not be byte-aligned.     *     * @param bits the bit string, MSB first     */    public void putTruncatedUnalignedBitString(BitArray ba) throws IOException {        putUnalignedBitString(ba.truncate());    }     /**     * DER-encodes an ASN.1 OCTET STRING value on the output stream.     *     * @param octets the octet string     */    public void putOctetString(byte[] octets) throws IOException {        write(DerValue.tag_OctetString, octets);    }     /**     * Marshals a DER "null" value on the output stream.  These are     * often used to indicate optional values which have been omitted.     */    public void putNull() throws IOException {        write(DerValue.tag_Null);        putLength(0);    }     /**     * Marshals an object identifier (OID) on the output stream.     * Corresponds to the ASN.1 "OBJECT IDENTIFIER" construct.     */    public void putOID(ObjectIdentifier oid) throws IOException {        oid.encode(this);    }     /**     * Marshals a sequence on the output stream.  This supports both     * the ASN.1 "SEQUENCE" (zero to N values) and "SEQUENCE OF"     * (one to N values) constructs.     */    public void putSequence(DerValue[] seq) throws IOException {        DerOutputStream bytes = new DerOutputStream();        int i;         for (i = 0; i < seq.length; i++)            seq[i].encode(bytes);         write(DerValue.tag_Sequence, bytes);    }     /**     * Marshals the contents of a set on the output stream without     * ordering the elements.  Ok for BER encoding, but not for DER     * encoding.     *     * For DER encoding, use orderedPutSet() or orderedPutSetOf().     */    public void putSet(DerValue[] set) throws IOException {        DerOutputStream bytes = new DerOutputStream();        int i;         for (i = 0; i < set.length; i++)            set[i].encode(bytes);         write(DerValue.tag_Set, bytes);    }     /**     * Marshals the contents of a set on the output stream.  Sets     * are semantically unordered, but DER requires that encodings of     * set elements be sorted into ascending lexicographical order     * before being output.  Hence sets with the same tags and     * elements have the same DER encoding.     *     * This method supports the ASN.1 "SET OF" construct, but not     * "SET", which uses a different order.     */    public void putOrderedSetOf(byte tag, DerEncoder[] set) throws IOException {        putOrderedSet(tag, set, lexOrder);    }     /**     * Marshals the contents of a set on the output stream.  Sets     * are semantically unordered, but DER requires that encodings of     * set elements be sorted into ascending tag order     * before being output.  Hence sets with the same tags and     * elements have the same DER encoding.     *     * This method supports the ASN.1 "SET" construct, but not     * "SET OF", which uses a different order.     */    public void putOrderedSet(byte tag, DerEncoder[] set) throws IOException {        putOrderedSet(tag, set, tagOrder);    }     /**     *  Lexicographical order comparison on byte arrays, for ordering     *  elements of a SET OF objects in DER encoding.     */    private static ByteArrayLexOrder lexOrder = new ByteArrayLexOrder();     /**     *  Tag order comparison on byte arrays, for ordering elements of     *  SET objects in DER encoding.     */    private static ByteArrayTagOrder tagOrder = new ByteArrayTagOrder();     /**     * Marshals a the contents of a set on the output stream with the     * encodings of its sorted in increasing order.     *     * @param order the order to use when sorting encodings of components.     */    private void putOrderedSet(byte tag, DerEncoder[] set,                               Comparator<byte[]> order) throws IOException {        DerOutputStream[] streams = new DerOutputStream[set.length];         for (int i = 0; i < set.length; i++) {            streams[i] = new DerOutputStream();            set[i].derEncode(streams[i]);        }         // order the element encodings        byte[][] bufs = new byte[streams.length][];        for (int i = 0; i < streams.length; i++) {            bufs[i] = streams[i].toByteArray();        }        Arrays.<byte[]>sort(bufs, order);         DerOutputStream bytes = new DerOutputStream();        for (int i = 0; i < streams.length; i++) {            bytes.write(bufs[i]);        }        write(tag, bytes);     }     /**     * Marshals a string as a DER encoded UTF8String.     */    public void putUTF8String(String s) throws IOException {        writeString(s, DerValue.tag_UTF8String, "UTF8");    }     /**     * Marshals a string as a DER encoded PrintableString.     */    public void putPrintableString(String s) throws IOException {        writeString(s, DerValue.tag_PrintableString, "ASCII");    }     /**     * Marshals a string as a DER encoded T61String.     */    public void putT61String(String s) throws IOException {        /*         * Works for characters that are defined in both ASCII and         * T61.         */        writeString(s, DerValue.tag_T61String, "ISO-8859-1");    }     /**     * Marshals a string as a DER encoded IA5String.     */    public void putIA5String(String s) throws IOException {        writeString(s, DerValue.tag_IA5String, "ASCII");    }     /**     * Marshals a string as a DER encoded BMPString.     */    public void putBMPString(String s) throws IOException {        writeString(s, DerValue.tag_BMPString, "UnicodeBigUnmarked");    }     /**     * Marshals a string as a DER encoded GeneralString.     */    public void putGeneralString(String s) throws IOException {        writeString(s, DerValue.tag_GeneralString, "ASCII");    }     /**     * Private helper routine for writing DER encoded string values.     * @param s the string to write     * @param stringTag one of the DER string tags that indicate which     * encoding should be used to write the string out.     * @param enc the name of the encoder that should be used corresponding     * to the above tag.     */    private void writeString(String s, byte stringTag, String enc)        throws IOException {         byte[] data = s.getBytes(enc);        write(stringTag);        putLength(data.length);        write(data);    }     /**     * Marshals a DER UTC time/date value.     *     * <P>YYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time     * and with seconds (even if seconds=0) as per RFC 3280.     */    public void putUTCTime(Date d) throws IOException {        putTime(d, DerValue.tag_UtcTime);    }     /**     * Marshals a DER Generalized Time/date value.     *     * <P>YYYYMMDDhhmmss{Z|+hhmm|-hhmm} ... emits only using Zulu time     * and with seconds (even if seconds=0) as per RFC 3280.     */    public void putGeneralizedTime(Date d) throws IOException {        putTime(d, DerValue.tag_GeneralizedTime);    }     /**     * Private helper routine for marshalling a DER UTC/Generalized     * time/date value. If the tag specified is not that for UTC Time     * then it defaults to Generalized Time.     * @param d the date to be marshalled     * @param tag the tag for UTC Time or Generalized Time     */    private void putTime(Date d, byte tag) throws IOException {         /*         * Format the date.         */         TimeZone tz = TimeZone.getTimeZone("GMT");        String pattern = null;         if (tag == DerValue.tag_UtcTime) {            pattern = "yyMMddHHmmss'Z'";        } else {            tag = DerValue.tag_GeneralizedTime;            pattern = "yyyyMMddHHmmss'Z'";

Lines 1–500 of 576.