0N/A/*
2362N/A * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/Apackage sun.security.util;
0N/A
0N/Aimport java.io.ByteArrayInputStream;
0N/Aimport java.io.IOException;
0N/Aimport java.io.OutputStream;
0N/Aimport java.math.BigInteger;
0N/Aimport java.util.Date;
0N/Aimport sun.util.calendar.CalendarDate;
0N/Aimport sun.util.calendar.CalendarSystem;
0N/A
0N/A/**
0N/A * DER input buffer ... this is the main abstraction in the DER library
0N/A * which actively works with the "untyped byte stream" abstraction. It
0N/A * does so with impunity, since it's not intended to be exposed to
0N/A * anyone who could violate the "typed value stream" DER model and hence
0N/A * corrupt the input stream of DER values.
0N/A *
0N/A * @author David Brownell
0N/A */
0N/Aclass DerInputBuffer extends ByteArrayInputStream implements Cloneable {
0N/A
0N/A DerInputBuffer(byte[] buf) { super(buf); }
0N/A
0N/A DerInputBuffer(byte[] buf, int offset, int len) {
0N/A super(buf, offset, len);
0N/A }
0N/A
0N/A DerInputBuffer dup() {
0N/A try {
0N/A DerInputBuffer retval = (DerInputBuffer)clone();
0N/A
0N/A retval.mark(Integer.MAX_VALUE);
0N/A return retval;
0N/A } catch (CloneNotSupportedException e) {
0N/A throw new IllegalArgumentException(e.toString());
0N/A }
0N/A }
0N/A
0N/A byte[] toByteArray() {
0N/A int len = available();
0N/A if (len <= 0)
0N/A return null;
0N/A byte[] retval = new byte[len];
0N/A
0N/A System.arraycopy(buf, pos, retval, 0, len);
0N/A return retval;
0N/A }
0N/A
0N/A int peek() throws IOException {
0N/A if (pos >= count)
0N/A throw new IOException("out of data");
0N/A else
0N/A return buf[pos];
0N/A }
0N/A
0N/A /**
0N/A * Compares this DerInputBuffer for equality with the specified
0N/A * object.
0N/A */
0N/A public boolean equals(Object other) {
0N/A if (other instanceof DerInputBuffer)
0N/A return equals((DerInputBuffer)other);
0N/A else
0N/A return false;
0N/A }
0N/A
0N/A boolean equals(DerInputBuffer other) {
0N/A if (this == other)
0N/A return true;
0N/A
0N/A int max = this.available();
0N/A if (other.available() != max)
0N/A return false;
0N/A for (int i = 0; i < max; i++) {
0N/A if (this.buf[this.pos + i] != other.buf[other.pos + i]) {
0N/A return false;
0N/A }
0N/A }
0N/A return true;
0N/A }
0N/A
0N/A /**
0N/A * Returns a hashcode for this DerInputBuffer.
0N/A *
0N/A * @return a hashcode for this DerInputBuffer.
0N/A */
0N/A public int hashCode() {
0N/A int retval = 0;
0N/A
0N/A int len = available();
0N/A int p = pos;
0N/A
0N/A for (int i = 0; i < len; i++)
0N/A retval += buf[p + i] * i;
0N/A return retval;
0N/A }
0N/A
0N/A void truncate(int len) throws IOException {
0N/A if (len > available())
0N/A throw new IOException("insufficient data");
0N/A count = pos + len;
0N/A }
0N/A
0N/A /**
0N/A * Returns the integer which takes up the specified number
0N/A * of bytes in this buffer as a BigInteger.
0N/A * @param len the number of bytes to use.
0N/A * @param makePositive whether to always return a positive value,
0N/A * irrespective of actual encoding
0N/A * @return the integer as a BigInteger.
0N/A */
0N/A BigInteger getBigInteger(int len, boolean makePositive) throws IOException {
0N/A if (len > available())
0N/A throw new IOException("short read of integer");
0N/A
0N/A if (len == 0) {
0N/A throw new IOException("Invalid encoding: zero length Int value");
0N/A }
0N/A
0N/A byte[] bytes = new byte[len];
0N/A
0N/A System.arraycopy(buf, pos, bytes, 0, len);
0N/A skip(len);
0N/A
0N/A if (makePositive) {
0N/A return new BigInteger(1, bytes);
0N/A } else {
0N/A return new BigInteger(bytes);
0N/A }
0N/A }
0N/A
0N/A /**
0N/A * Returns the integer which takes up the specified number
0N/A * of bytes in this buffer.
0N/A * @throws IOException if the result is not within the valid
0N/A * range for integer, i.e. between Integer.MIN_VALUE and
0N/A * Integer.MAX_VALUE.
0N/A * @param len the number of bytes to use.
0N/A * @return the integer.
0N/A */
0N/A public int getInteger(int len) throws IOException {
0N/A
0N/A BigInteger result = getBigInteger(len, false);
0N/A if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {
0N/A throw new IOException("Integer below minimum valid value");
0N/A }
0N/A if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
0N/A throw new IOException("Integer exceeds maximum valid value");
0N/A }
0N/A return result.intValue();
0N/A }
0N/A
0N/A /**
0N/A * Returns the bit string which takes up the specified
0N/A * number of bytes in this buffer.
0N/A */
0N/A public byte[] getBitString(int len) throws IOException {
0N/A if (len > available())
0N/A throw new IOException("short read of bit string");
0N/A
0N/A if (len == 0) {
0N/A throw new IOException("Invalid encoding: zero length bit string");
0N/A }
0N/A
0N/A int numOfPadBits = buf[pos];
0N/A if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
0N/A throw new IOException("Invalid number of padding bits");
0N/A }
0N/A // minus the first byte which indicates the number of padding bits
0N/A byte[] retval = new byte[len - 1];
0N/A System.arraycopy(buf, pos + 1, retval, 0, len - 1);
0N/A if (numOfPadBits != 0) {
0N/A // get rid of the padding bits
0N/A retval[len - 2] &= (0xff << numOfPadBits);
0N/A }
0N/A skip(len);
0N/A return retval;
0N/A }
0N/A
0N/A /**
0N/A * Returns the bit string which takes up the rest of this buffer.
0N/A */
0N/A byte[] getBitString() throws IOException {
0N/A return getBitString(available());
0N/A }
0N/A
0N/A /**
0N/A * Returns the bit string which takes up the rest of this buffer.
0N/A * The bit string need not be byte-aligned.
0N/A */
0N/A BitArray getUnalignedBitString() throws IOException {
0N/A if (pos >= count)
0N/A return null;
0N/A /*
0N/A * Just copy the data into an aligned, padded octet buffer,
0N/A * and consume the rest of the buffer.
0N/A */
0N/A int len = available();
0N/A int unusedBits = buf[pos] & 0xff;
0N/A if (unusedBits > 7 ) {
0N/A throw new IOException("Invalid value for unused bits: " + unusedBits);
0N/A }
0N/A byte[] bits = new byte[len - 1];
0N/A // number of valid bits
0N/A int length = (bits.length == 0) ? 0 : bits.length * 8 - unusedBits;
0N/A
0N/A System.arraycopy(buf, pos + 1, bits, 0, len - 1);
0N/A
0N/A BitArray bitArray = new BitArray(length, bits);
0N/A pos = count;
0N/A return bitArray;
0N/A }
0N/A
0N/A /**
0N/A * Returns the UTC Time value that takes up the specified number
0N/A * of bytes in this buffer.
0N/A * @param len the number of bytes to use
0N/A */
0N/A public Date getUTCTime(int len) throws IOException {
0N/A if (len > available())
0N/A throw new IOException("short read of DER UTC Time");
0N/A
0N/A if (len < 11 || len > 17)
0N/A throw new IOException("DER UTC Time length error");
0N/A
0N/A return getTime(len, false);
0N/A }
0N/A
0N/A /**
0N/A * Returns the Generalized Time value that takes up the specified
0N/A * number of bytes in this buffer.
0N/A * @param len the number of bytes to use
0N/A */
0N/A public Date getGeneralizedTime(int len) throws IOException {
0N/A if (len > available())
0N/A throw new IOException("short read of DER Generalized Time");
0N/A
0N/A if (len < 13 || len > 23)
0N/A throw new IOException("DER Generalized Time length error");
0N/A
0N/A return getTime(len, true);
0N/A
0N/A }
0N/A
0N/A /**
0N/A * Private helper routine to extract time from the der value.
0N/A * @param len the number of bytes to use
0N/A * @param generalized true if Generalized Time is to be read, false
0N/A * if UTC Time is to be read.
0N/A */
0N/A private Date getTime(int len, boolean generalized) throws IOException {
0N/A
0N/A /*
0N/A * UTC time encoded as ASCII chars:
0N/A * YYMMDDhhmmZ
0N/A * YYMMDDhhmmssZ
0N/A * YYMMDDhhmm+hhmm
0N/A * YYMMDDhhmm-hhmm
0N/A * YYMMDDhhmmss+hhmm
0N/A * YYMMDDhhmmss-hhmm
0N/A * UTC Time is broken in storing only two digits of year.
0N/A * If YY < 50, we assume 20YY;
0N/A * if YY >= 50, we assume 19YY, as per RFC 3280.
0N/A *
0N/A * Generalized time has a four-digit year and allows any
0N/A * precision specified in ISO 8601. However, for our purposes,
0N/A * we will only allow the same format as UTC time, except that
0N/A * fractional seconds (millisecond precision) are supported.
0N/A */
0N/A
0N/A int year, month, day, hour, minute, second, millis;
0N/A String type = null;
0N/A
0N/A if (generalized) {
0N/A type = "Generalized";
0N/A year = 1000 * Character.digit((char)buf[pos++], 10);
0N/A year += 100 * Character.digit((char)buf[pos++], 10);
0N/A year += 10 * Character.digit((char)buf[pos++], 10);
0N/A year += Character.digit((char)buf[pos++], 10);
0N/A len -= 2; // For the two extra YY
0N/A } else {
0N/A type = "UTC";
0N/A year = 10 * Character.digit((char)buf[pos++], 10);
0N/A year += Character.digit((char)buf[pos++], 10);
0N/A
0N/A if (year < 50) // origin 2000
0N/A year += 2000;
0N/A else
0N/A year += 1900; // origin 1900
0N/A }
0N/A
0N/A month = 10 * Character.digit((char)buf[pos++], 10);
0N/A month += Character.digit((char)buf[pos++], 10);
0N/A
0N/A day = 10 * Character.digit((char)buf[pos++], 10);
0N/A day += Character.digit((char)buf[pos++], 10);
0N/A
0N/A hour = 10 * Character.digit((char)buf[pos++], 10);
0N/A hour += Character.digit((char)buf[pos++], 10);
0N/A
0N/A minute = 10 * Character.digit((char)buf[pos++], 10);
0N/A minute += Character.digit((char)buf[pos++], 10);
0N/A
0N/A len -= 10; // YYMMDDhhmm
0N/A
0N/A /*
0N/A * We allow for non-encoded seconds, even though the
0N/A * IETF-PKIX specification says that the seconds should
0N/A * always be encoded even if it is zero.
0N/A */
0N/A
0N/A millis = 0;
0N/A if (len > 2 && len < 12) {
0N/A second = 10 * Character.digit((char)buf[pos++], 10);
0N/A second += Character.digit((char)buf[pos++], 10);
0N/A len -= 2;
0N/A // handle fractional seconds (if present)
0N/A if (buf[pos] == '.' || buf[pos] == ',') {
0N/A len --;
0N/A pos++;
0N/A // handle upto milisecond precision only
0N/A int precision = 0;
0N/A int peek = pos;
0N/A while (buf[peek] != 'Z' &&
0N/A buf[peek] != '+' &&
0N/A buf[peek] != '-') {
0N/A peek++;
0N/A precision++;
0N/A }
0N/A switch (precision) {
0N/A case 3:
0N/A millis += 100 * Character.digit((char)buf[pos++], 10);
0N/A millis += 10 * Character.digit((char)buf[pos++], 10);
0N/A millis += Character.digit((char)buf[pos++], 10);
0N/A break;
0N/A case 2:
0N/A millis += 100 * Character.digit((char)buf[pos++], 10);
0N/A millis += 10 * Character.digit((char)buf[pos++], 10);
0N/A break;
0N/A case 1:
0N/A millis += 100 * Character.digit((char)buf[pos++], 10);
0N/A break;
0N/A default:
0N/A throw new IOException("Parse " + type +
0N/A " time, unsupported precision for seconds value");
0N/A }
0N/A len -= precision;
0N/A }
0N/A } else
0N/A second = 0;
0N/A
0N/A if (month == 0 || day == 0
0N/A || month > 12 || day > 31
0N/A || hour >= 24 || minute >= 60 || second >= 60)
0N/A throw new IOException("Parse " + type + " time, invalid format");
0N/A
0N/A /*
0N/A * Generalized time can theoretically allow any precision,
0N/A * but we're not supporting that.
0N/A */
0N/A CalendarSystem gcal = CalendarSystem.getGregorianCalendar();
0N/A CalendarDate date = gcal.newCalendarDate(null); // no time zone
0N/A date.setDate(year, month, day);
0N/A date.setTimeOfDay(hour, minute, second, millis);
0N/A long time = gcal.getTime(date);
0N/A
0N/A /*
0N/A * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm
0N/A */
0N/A if (! (len == 1 || len == 5))
0N/A throw new IOException("Parse " + type + " time, invalid offset");
0N/A
0N/A int hr, min;
0N/A
0N/A switch (buf[pos++]) {
0N/A case '+':
0N/A hr = 10 * Character.digit((char)buf[pos++], 10);
0N/A hr += Character.digit((char)buf[pos++], 10);
0N/A min = 10 * Character.digit((char)buf[pos++], 10);
0N/A min += Character.digit((char)buf[pos++], 10);
0N/A
0N/A if (hr >= 24 || min >= 60)
0N/A throw new IOException("Parse " + type + " time, +hhmm");
0N/A
0N/A time -= ((hr * 60) + min) * 60 * 1000;
0N/A break;
0N/A
0N/A case '-':
0N/A hr = 10 * Character.digit((char)buf[pos++], 10);
0N/A hr += Character.digit((char)buf[pos++], 10);
0N/A min = 10 * Character.digit((char)buf[pos++], 10);
0N/A min += Character.digit((char)buf[pos++], 10);
0N/A
0N/A if (hr >= 24 || min >= 60)
0N/A throw new IOException("Parse " + type + " time, -hhmm");
0N/A
0N/A time += ((hr * 60) + min) * 60 * 1000;
0N/A break;
0N/A
0N/A case 'Z':
0N/A break;
0N/A
0N/A default:
0N/A throw new IOException("Parse " + type + " time, garbage offset");
0N/A }
0N/A return new Date(time);
0N/A }
0N/A}