934 lines · 29.9 KB · Java
RawDownload
/* * Copyright (c) 1996, 2009, 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.*;import java.math.BigInteger;import java.util.Date;import sun.misc.IOUtils; /** * Represents a single DER-encoded value.  DER encoding rules are a subset * of the "Basic" Encoding Rules (BER), but they only support a single way * ("Definite" encoding) to encode any given value. * * <P>All DER-encoded data are triples <em>{type, length, data}</em>.  This * class represents such tagged values as they have been read (or constructed), * and provides structured access to the encoded data. * * <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 parsing * most X.509 certificates, and working with selected additional formats * (such as PKCS #10 certificate requests, and some kinds of PKCS #7 data). * * A note with respect to T61/Teletex strings: From RFC 1617, section 4.1.3 * and RFC 3280, section 4.1.2.4., we assume that this kind of string will * contain ISO-8859-1 characters only. * * * @author David Brownell * @author Amit Kapoor * @author Hemma Prafullchandra */public class DerValue {    /** The tag class types */    public static final byte TAG_UNIVERSAL = (byte)0x000;    public static final byte TAG_APPLICATION = (byte)0x040;    public static final byte TAG_CONTEXT = (byte)0x080;    public static final byte TAG_PRIVATE = (byte)0x0c0;     /** The DER tag of the value; one of the tag_ constants. */    public byte                 tag;     protected DerInputBuffer    buffer;     /**     * The DER-encoded data of the value, never null     */    public final DerInputStream data;     private int                 length;     /*     * The type starts at the first byte of the encoding, and     * is one of these tag_* values.  That may be all the type     * data that is needed.     */     /*     * These tags are the "universal" tags ... they mean the same     * in all contexts.  (Mask with 0x1f -- five bits.)     */     /** Tag value indicating an ASN.1 "BOOLEAN" value. */    public final static byte    tag_Boolean = 0x01;     /** Tag value indicating an ASN.1 "INTEGER" value. */    public final static byte    tag_Integer = 0x02;     /** Tag value indicating an ASN.1 "BIT STRING" value. */    public final static byte    tag_BitString = 0x03;     /** Tag value indicating an ASN.1 "OCTET STRING" value. */    public final static byte    tag_OctetString = 0x04;     /** Tag value indicating an ASN.1 "NULL" value. */    public final static byte    tag_Null = 0x05;     /** Tag value indicating an ASN.1 "OBJECT IDENTIFIER" value. */    public final static byte    tag_ObjectId = 0x06;     /** Tag value including an ASN.1 "ENUMERATED" value */    public final static byte    tag_Enumerated = 0x0A;     /** Tag value indicating an ASN.1 "UTF8String" value. */    public final static byte    tag_UTF8String = 0x0C;     /** Tag value including a "printable" string */    public final static byte    tag_PrintableString = 0x13;     /** Tag value including a "teletype" string */    public final static byte    tag_T61String = 0x14;     /** Tag value including an ASCII string */    public final static byte    tag_IA5String = 0x16;     /** Tag value indicating an ASN.1 "UTCTime" value. */    public final static byte    tag_UtcTime = 0x17;     /** Tag value indicating an ASN.1 "GeneralizedTime" value. */    public final static byte    tag_GeneralizedTime = 0x18;     /** Tag value indicating an ASN.1 "GenerallString" value. */    public final static byte    tag_GeneralString = 0x1B;     /** Tag value indicating an ASN.1 "UniversalString" value. */    public final static byte    tag_UniversalString = 0x1C;     /** Tag value indicating an ASN.1 "BMPString" value. */    public final static byte    tag_BMPString = 0x1E;     // CONSTRUCTED seq/set     /**     * Tag value indicating an ASN.1     * "SEQUENCE" (zero to N elements, order is significant).     */    public final static byte    tag_Sequence = 0x30;     /**     * Tag value indicating an ASN.1     * "SEQUENCE OF" (one to N elements, order is significant).     */    public final static byte    tag_SequenceOf = 0x30;     /**     * Tag value indicating an ASN.1     * "SET" (zero to N members, order does not matter).     */    public final static byte    tag_Set = 0x31;     /**     * Tag value indicating an ASN.1     * "SET OF" (one to N members, order does not matter).     */    public final static byte    tag_SetOf = 0x31;     /*     * These values are the high order bits for the other kinds of tags.     */     /**     * Returns true if the tag class is UNIVERSAL.     */    public boolean isUniversal()      { return ((tag & 0x0c0) == 0x000); }     /**     * Returns true if the tag class is APPLICATION.     */    public boolean isApplication()    { return ((tag & 0x0c0) == 0x040); }     /**     * Returns true iff the CONTEXT SPECIFIC bit is set in the type tag.     * This is associated with the ASN.1 "DEFINED BY" syntax.     */    public boolean isContextSpecific() { return ((tag & 0x0c0) == 0x080); }     /**     * Returns true iff the CONTEXT SPECIFIC TAG matches the passed tag.     */    public boolean isContextSpecific(byte cntxtTag) {        if (!isContextSpecific()) {            return false;        }        return ((tag & 0x01f) == cntxtTag);    }     boolean isPrivate()        { return ((tag & 0x0c0) == 0x0c0); }     /** Returns true iff the CONSTRUCTED bit is set in the type tag. */    public boolean isConstructed()    { return ((tag & 0x020) == 0x020); }     /**     * Returns true iff the CONSTRUCTED TAG matches the passed tag.     */    public boolean isConstructed(byte constructedTag) {        if (!isConstructed()) {            return false;        }        return ((tag & 0x01f) == constructedTag);    }     /**     * Creates a PrintableString or UTF8string DER value from a string     */    public DerValue(String value) throws IOException {        boolean isPrintableString = true;        for (int i = 0; i < value.length(); i++) {            if (!isPrintableStringChar(value.charAt(i))) {                isPrintableString = false;                break;            }        }         data = init(isPrintableString ? tag_PrintableString : tag_UTF8String, value);    }     /**     * Creates a string type DER value from a String object     * @param stringTag the tag for the DER value to create     * @param value the String object to use for the DER value     */    public DerValue(byte stringTag, String value) throws IOException {        data = init(stringTag, value);    }     /**     * Creates a DerValue from a tag and some DER-encoded data.     *     * @param tag the DER type tag     * @param data the DER-encoded data     */    public DerValue(byte tag, byte[] data) {        this.tag = tag;        buffer = new DerInputBuffer(data.clone());        length = data.length;        this.data = new DerInputStream(buffer);        this.data.mark(Integer.MAX_VALUE);    }     /*     * package private     */    DerValue(DerInputBuffer in) throws IOException {        // XXX must also parse BER-encoded constructed        // values such as sequences, sets...         tag = (byte)in.read();        byte lenByte = (byte)in.read();        length = DerInputStream.getLength((lenByte & 0xff), in);        if (length == -1) {  // indefinite length encoding found            DerInputBuffer inbuf = in.dup();            int readLen = inbuf.available();            int offset = 2;     // for tag and length bytes            byte[] indefData = new byte[readLen + offset];            indefData[0] = tag;            indefData[1] = lenByte;            DataInputStream dis = new DataInputStream(inbuf);            dis.readFully(indefData, offset, readLen);            dis.close();            DerIndefLenConverter derIn = new DerIndefLenConverter();            inbuf = new DerInputBuffer(derIn.convert(indefData));            if (tag != inbuf.read())                throw new IOException                        ("Indefinite length encoding not supported");            length = DerInputStream.getLength(inbuf);            buffer = inbuf.dup();            buffer.truncate(length);            data = new DerInputStream(buffer);            // indefinite form is encoded by sending a length field with a            // length of 0. - i.e. [1000|0000].            // the object is ended by sending two zero bytes.            in.skip(length + offset);        } else {             buffer = in.dup();            buffer.truncate(length);            data = new DerInputStream(buffer);             in.skip(length);        }    }     /**     * Get an ASN.1/DER encoded datum from a buffer.  The     * entire buffer must hold exactly one datum, including     * its tag and length.     *     * @param buf buffer holding a single DER-encoded datum.     */    public DerValue(byte[] buf) throws IOException {        data = init(true, new ByteArrayInputStream(buf));    }     /**     * Get an ASN.1/DER encoded datum from part of a buffer.     * That part of the buffer must hold exactly one datum, including     * its tag and length.     *     * @param buf the buffer     * @param offset start point of the single DER-encoded dataum     * @param length how many bytes are in the encoded datum     */    public DerValue(byte[] buf, int offset, int len) throws IOException {        data = init(true, new ByteArrayInputStream(buf, offset, len));    }     /**     * Get an ASN1/DER encoded datum from an input stream.  The     * stream may have additional data following the encoded datum.     * In case of indefinite length encoded datum, the input stream     * must hold only one datum.     *     * @param in the input stream holding a single DER datum,     *  which may be followed by additional data     */    public DerValue(InputStream in) throws IOException {        data = init(false, in);    }     private DerInputStream init(byte stringTag, String value) throws IOException {        String enc = null;         tag = stringTag;         switch (stringTag) {        case tag_PrintableString:        case tag_IA5String:        case tag_GeneralString:            enc = "ASCII";            break;        case tag_T61String:            enc = "ISO-8859-1";            break;        case tag_BMPString:            enc = "UnicodeBigUnmarked";            break;        case tag_UTF8String:            enc = "UTF8";            break;            // TBD: Need encoder for UniversalString before it can            // be handled.        default:            throw new IllegalArgumentException("Unsupported DER string type");        }         byte[] buf = value.getBytes(enc);        length = buf.length;        buffer = new DerInputBuffer(buf);        DerInputStream result = new DerInputStream(buffer);        result.mark(Integer.MAX_VALUE);        return result;    }     /*     * helper routine     */    private DerInputStream init(boolean fullyBuffered, InputStream in)            throws IOException {         tag = (byte)in.read();        byte lenByte = (byte)in.read();        length = DerInputStream.getLength((lenByte & 0xff), in);        if (length == -1) { // indefinite length encoding found            int readLen = in.available();            int offset = 2;     // for tag and length bytes            byte[] indefData = new byte[readLen + offset];            indefData[0] = tag;            indefData[1] = lenByte;            DataInputStream dis = new DataInputStream(in);            dis.readFully(indefData, offset, readLen);            dis.close();            DerIndefLenConverter derIn = new DerIndefLenConverter();            in = new ByteArrayInputStream(derIn.convert(indefData));            if (tag != in.read())                throw new IOException                        ("Indefinite length encoding not supported");            length = DerInputStream.getLength(in);        }         if (fullyBuffered && in.available() != length)            throw new IOException("extra data given to DerValue constructor");         byte[] bytes = IOUtils.readFully(in, length, true);         buffer = new DerInputBuffer(bytes);        return new DerInputStream(buffer);    }     /**     * Encode an ASN1/DER encoded datum onto a DER output stream.     */    public void encode(DerOutputStream out)    throws IOException {        out.write(tag);        out.putLength(length);        // XXX yeech, excess copies ... DerInputBuffer.write(OutStream)        if (length > 0) {            byte[] value = new byte[length];            // always synchronized on data            synchronized (data) {                buffer.reset();                if (buffer.read(value) != length) {                    throw new IOException("short DER value read (encode)");                }                out.write(value);            }        }    }     public final DerInputStream getData() {        return data;    }     public final byte getTag() {        return tag;    }     /**     * Returns an ASN.1 BOOLEAN     *     * @return the boolean held in this DER value     */    public boolean getBoolean() throws IOException {        if (tag != tag_Boolean) {            throw new IOException("DerValue.getBoolean, not a BOOLEAN " + tag);        }        if (length != 1) {            throw new IOException("DerValue.getBoolean, invalid length "                                        + length);        }        if (buffer.read() != 0) {            return true;        }        return false;    }     /**     * Returns an ASN.1 OBJECT IDENTIFIER.     *     * @return the OID held in this DER value     */    public ObjectIdentifier getOID() throws IOException {        if (tag != tag_ObjectId)            throw new IOException("DerValue.getOID, not an OID " + tag);        return new ObjectIdentifier(buffer);    }     private byte[] append(byte[] a, byte[] b) {        if (a == null)            return b;         byte[] ret = new byte[a.length + b.length];        System.arraycopy(a, 0, ret, 0, a.length);        System.arraycopy(b, 0, ret, a.length, b.length);         return ret;    }     /**     * Returns an ASN.1 OCTET STRING     *     * @return the octet string held in this DER value     */    public byte[] getOctetString() throws IOException {        byte[] bytes;         if (tag != tag_OctetString && !isConstructed(tag_OctetString)) {            throw new IOException(                "DerValue.getOctetString, not an Octet String: " + tag);        }        bytes = new byte[length];        // Note: do not tempt to call buffer.read(bytes) at all. There's a        // known bug that it returns -1 instead of 0.        if (length == 0) {            return bytes;        }        if (buffer.read(bytes) != length)            throw new IOException("short read on DerValue buffer");        if (isConstructed()) {            DerInputStream in = new DerInputStream(bytes);            bytes = null;            while (in.available() != 0) {                bytes = append(bytes, in.getOctetString());            }        }        return bytes;    }     /**     * Returns an ASN.1 INTEGER value as an integer.     *     * @return the integer held in this DER value.     */    public int getInteger() throws IOException {        if (tag != tag_Integer) {            throw new IOException("DerValue.getInteger, not an int " + tag);        }

Lines 1–500 of 934.