433 lines · 14.2 KB · Java
RawDownload
/* * Copyright (c) 1996, 2006, 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.ByteArrayInputStream;import java.io.IOException;import java.io.OutputStream;import java.math.BigInteger;import java.util.Date;import sun.util.calendar.CalendarDate;import sun.util.calendar.CalendarSystem; /** * DER input buffer ... this is the main abstraction in the DER library * which actively works with the "untyped byte stream" abstraction.  It * does so with impunity, since it's not intended to be exposed to * anyone who could violate the "typed value stream" DER model and hence * corrupt the input stream of DER values. * * @author David Brownell */class DerInputBuffer extends ByteArrayInputStream implements Cloneable {     DerInputBuffer(byte[] buf) { super(buf); }     DerInputBuffer(byte[] buf, int offset, int len) {        super(buf, offset, len);    }     DerInputBuffer dup() {        try {            DerInputBuffer retval = (DerInputBuffer)clone();             retval.mark(Integer.MAX_VALUE);            return retval;        } catch (CloneNotSupportedException e) {            throw new IllegalArgumentException(e.toString());        }    }     byte[] toByteArray() {        int     len = available();        if (len <= 0)            return null;        byte[]  retval = new byte[len];         System.arraycopy(buf, pos, retval, 0, len);        return retval;    }     int peek() throws IOException {        if (pos >= count)            throw new IOException("out of data");        else            return buf[pos];    }     /**     * Compares this DerInputBuffer for equality with the specified     * object.     */    public boolean equals(Object other) {        if (other instanceof DerInputBuffer)            return equals((DerInputBuffer)other);        else            return false;    }     boolean equals(DerInputBuffer other) {        if (this == other)            return true;         int max = this.available();        if (other.available() != max)            return false;        for (int i = 0; i < max; i++) {            if (this.buf[this.pos + i] != other.buf[other.pos + i]) {                return false;            }        }        return true;    }     /**     * Returns a hashcode for this DerInputBuffer.     *     * @return a hashcode for this DerInputBuffer.     */    public int hashCode() {        int retval = 0;         int len = available();        int p = pos;         for (int i = 0; i < len; i++)            retval += buf[p + i] * i;        return retval;    }     void truncate(int len) throws IOException {        if (len > available())            throw new IOException("insufficient data");        count = pos + len;    }     /**     * Returns the integer which takes up the specified number     * of bytes in this buffer as a BigInteger.     * @param len the number of bytes to use.     * @param makePositive whether to always return a positive value,     *   irrespective of actual encoding     * @return the integer as a BigInteger.     */    BigInteger getBigInteger(int len, boolean makePositive) throws IOException {        if (len > available())            throw new IOException("short read of integer");         if (len == 0) {            throw new IOException("Invalid encoding: zero length Int value");        }         byte[] bytes = new byte[len];         System.arraycopy(buf, pos, bytes, 0, len);        skip(len);         if (makePositive) {            return new BigInteger(1, bytes);        } else {            return new BigInteger(bytes);        }    }     /**     * Returns the integer which takes up the specified number     * of bytes in this buffer.     * @throws IOException if the result is not within the valid     * range for integer, i.e. between Integer.MIN_VALUE and     * Integer.MAX_VALUE.     * @param len the number of bytes to use.     * @return the integer.     */    public int getInteger(int len) throws IOException {         BigInteger result = getBigInteger(len, false);        if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {            throw new IOException("Integer below minimum valid value");        }        if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {            throw new IOException("Integer exceeds maximum valid value");        }        return result.intValue();    }     /**     * Returns the bit string which takes up the specified     * number of bytes in this buffer.     */    public byte[] getBitString(int len) throws IOException {        if (len > available())            throw new IOException("short read of bit string");         if (len == 0) {            throw new IOException("Invalid encoding: zero length bit string");        }         int numOfPadBits = buf[pos];        if ((numOfPadBits < 0) || (numOfPadBits > 7)) {            throw new IOException("Invalid number of padding bits");        }        // minus the first byte which indicates the number of padding bits        byte[] retval = new byte[len - 1];        System.arraycopy(buf, pos + 1, retval, 0, len - 1);        if (numOfPadBits != 0) {            // get rid of the padding bits            retval[len - 2] &= (0xff << numOfPadBits);        }        skip(len);        return retval;    }     /**     * Returns the bit string which takes up the rest of this buffer.     */    byte[] getBitString() throws IOException {        return getBitString(available());    }     /**     * Returns the bit string which takes up the rest of this buffer.     * The bit string need not be byte-aligned.     */    BitArray getUnalignedBitString() throws IOException {        if (pos >= count)            return null;        /*         * Just copy the data into an aligned, padded octet buffer,         * and consume the rest of the buffer.         */        int len = available();        int unusedBits = buf[pos] & 0xff;        if (unusedBits > 7 ) {            throw new IOException("Invalid value for unused bits: " + unusedBits);        }        byte[] bits = new byte[len - 1];        // number of valid bits        int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits;         System.arraycopy(buf, pos + 1, bits, 0, len - 1);         BitArray bitArray = new BitArray(length, bits);        pos = count;        return bitArray;    }     /**     * Returns the UTC Time value that takes up the specified number     * of bytes in this buffer.     * @param len the number of bytes to use     */    public Date getUTCTime(int len) throws IOException {        if (len > available())            throw new IOException("short read of DER UTC Time");         if (len < 11 || len > 17)            throw new IOException("DER UTC Time length error");         return getTime(len, false);    }     /**     * Returns the Generalized Time value that takes up the specified     * number of bytes in this buffer.     * @param len the number of bytes to use     */    public Date getGeneralizedTime(int len) throws IOException {        if (len > available())            throw new IOException("short read of DER Generalized Time");         if (len < 13 || len > 23)            throw new IOException("DER Generalized Time length error");         return getTime(len, true);     }     /**     * Private helper routine to extract time from the der value.     * @param len the number of bytes to use     * @param generalized true if Generalized Time is to be read, false     * if UTC Time is to be read.     */    private Date getTime(int len, boolean generalized) throws IOException {         /*         * UTC time encoded as ASCII chars:         *       YYMMDDhhmmZ         *       YYMMDDhhmmssZ         *       YYMMDDhhmm+hhmm         *       YYMMDDhhmm-hhmm         *       YYMMDDhhmmss+hhmm         *       YYMMDDhhmmss-hhmm         * UTC Time is broken in storing only two digits of year.         * If YY < 50, we assume 20YY;         * if YY >= 50, we assume 19YY, as per RFC 3280.         *         * Generalized time has a four-digit year and allows any         * precision specified in ISO 8601. However, for our purposes,         * we will only allow the same format as UTC time, except that         * fractional seconds (millisecond precision) are supported.         */         int year, month, day, hour, minute, second, millis;        String type = null;         if (generalized) {            type = "Generalized";            year = 1000 * Character.digit((char)buf[pos++], 10);            year += 100 * Character.digit((char)buf[pos++], 10);            year += 10 * Character.digit((char)buf[pos++], 10);            year += Character.digit((char)buf[pos++], 10);            len -= 2; // For the two extra YY        } else {            type = "UTC";            year = 10 * Character.digit((char)buf[pos++], 10);            year += Character.digit((char)buf[pos++], 10);             if (year < 50)              // origin 2000                year += 2000;            else                year += 1900;   // origin 1900        }         month = 10 * Character.digit((char)buf[pos++], 10);        month += Character.digit((char)buf[pos++], 10);         day = 10 * Character.digit((char)buf[pos++], 10);        day += Character.digit((char)buf[pos++], 10);         hour = 10 * Character.digit((char)buf[pos++], 10);        hour += Character.digit((char)buf[pos++], 10);         minute = 10 * Character.digit((char)buf[pos++], 10);        minute += Character.digit((char)buf[pos++], 10);         len -= 10; // YYMMDDhhmm         /*         * We allow for non-encoded seconds, even though the         * IETF-PKIX specification says that the seconds should         * always be encoded even if it is zero.         */         millis = 0;        if (len > 2 && len < 12) {            second = 10 * Character.digit((char)buf[pos++], 10);            second += Character.digit((char)buf[pos++], 10);            len -= 2;            // handle fractional seconds (if present)            if (buf[pos] == '.' || buf[pos] == ',') {                len --;                pos++;                // handle upto milisecond precision only                int precision = 0;                int peek = pos;                while (buf[peek] != 'Z' &&                       buf[peek] != '+' &&                       buf[peek] != '-') {                    peek++;                    precision++;                }                switch (precision) {                case 3:                    millis += 100 * Character.digit((char)buf[pos++], 10);                    millis += 10 * Character.digit((char)buf[pos++], 10);                    millis += Character.digit((char)buf[pos++], 10);                    break;                case 2:                    millis += 100 * Character.digit((char)buf[pos++], 10);                    millis += 10 * Character.digit((char)buf[pos++], 10);                    break;                case 1:                    millis += 100 * Character.digit((char)buf[pos++], 10);                    break;                default:                        throw new IOException("Parse " + type +                            " time, unsupported precision for seconds value");                }                len -= precision;            }        } else            second = 0;         if (month == 0 || day == 0            || month > 12 || day > 31            || hour >= 24 || minute >= 60 || second >= 60)            throw new IOException("Parse " + type + " time, invalid format");         /*         * Generalized time can theoretically allow any precision,         * but we're not supporting that.         */        CalendarSystem gcal = CalendarSystem.getGregorianCalendar();        CalendarDate date = gcal.newCalendarDate(null); // no time zone        date.setDate(year, month, day);        date.setTimeOfDay(hour, minute, second, millis);        long time = gcal.getTime(date);         /*         * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm         */        if (! (len == 1 || len == 5))            throw new IOException("Parse " + type + " time, invalid offset");         int hr, min;         switch (buf[pos++]) {        case '+':            hr = 10 * Character.digit((char)buf[pos++], 10);            hr += Character.digit((char)buf[pos++], 10);            min = 10 * Character.digit((char)buf[pos++], 10);            min += Character.digit((char)buf[pos++], 10);             if (hr >= 24 || min >= 60)                throw new IOException("Parse " + type + " time, +hhmm");             time -= ((hr * 60) + min) * 60 * 1000;            break;         case '-':            hr = 10 * Character.digit((char)buf[pos++], 10);            hr += Character.digit((char)buf[pos++], 10);            min = 10 * Character.digit((char)buf[pos++], 10);            min += Character.digit((char)buf[pos++], 10);             if (hr >= 24 || min >= 60)                throw new IOException("Parse " + type + " time, -hhmm");             time += ((hr * 60) + min) * 60 * 1000;            break;         case 'Z':            break;         default:            throw new IOException("Parse " + type + " time, garbage offset");        }        return new Date(time);    }}