001: /*
002: * @(#)DerInputBuffer.java 1.24 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.security.util;
029:
030: import java.io.ByteArrayInputStream;
031: import java.io.IOException;
032: import java.io.OutputStream;
033: import java.math.BigInteger;
034: import java.util.Date;
035: import sun.util.calendar.CalendarDate;
036: import sun.util.calendar.Gregorian;
037:
038: /**
039: * DER input buffer ... this is the main abstraction in the DER library
040: * which actively works with the "untyped byte stream" abstraction. It
041: * does so with impunity, since it's not intended to be exposed to
042: * anyone who could violate the "typed value stream" DER model and hence
043: * corrupt the input stream of DER values.
044: *
045: * @version 1.17
046: * @author David Brownell
047: */
048: class DerInputBuffer extends ByteArrayInputStream implements Cloneable {
049:
050: DerInputBuffer(byte[] buf) {
051: super (buf);
052: }
053:
054: DerInputBuffer(byte[] buf, int offset, int len) {
055: super (buf, offset, len);
056: }
057:
058: DerInputBuffer dup() {
059: try {
060: DerInputBuffer retval = (DerInputBuffer) clone();
061:
062: retval.mark(Integer.MAX_VALUE);
063: return retval;
064: } catch (CloneNotSupportedException e) {
065: throw new IllegalArgumentException(e.toString());
066: }
067: }
068:
069: byte[] toByteArray() {
070: int len = available();
071: if (len <= 0)
072: return null;
073: byte[] retval = new byte[len];
074:
075: System.arraycopy(buf, pos, retval, 0, len);
076: return retval;
077: }
078:
079: int peek() throws IOException {
080: if (pos >= count)
081: throw new IOException("out of data");
082: else
083: return buf[pos];
084: }
085:
086: /**
087: * Compares this DerInputBuffer for equality with the specified
088: * object.
089: */
090: public boolean equals(Object other) {
091: if (other instanceof DerInputBuffer)
092: return equals((DerInputBuffer) other);
093: else
094: return false;
095: }
096:
097: boolean equals(DerInputBuffer other) {
098: if (this == other)
099: return true;
100:
101: int max = this .available();
102: if (other.available() != max)
103: return false;
104: for (int i = 0; i < max; i++) {
105: if (this .buf[this .pos + i] != other.buf[other.pos + i]) {
106: return false;
107: }
108: }
109: return true;
110: }
111:
112: /**
113: * Returns a hashcode for this DerInputBuffer.
114: *
115: * @return a hashcode for this DerInputBuffer.
116: */
117: public int hashCode() {
118: int retval = 0;
119:
120: int len = available();
121: int p = pos;
122:
123: for (int i = 0; i < len; i++)
124: retval += buf[p + i] * i;
125: return retval;
126: }
127:
128: void truncate(int len) throws IOException {
129: if (len > available())
130: throw new IOException("insufficient data");
131: count = pos + len;
132: }
133:
134: /**
135: * Returns the integer which takes up the specified number
136: * of bytes in this buffer as a BigInteger.
137: * @param len the number of bytes to use.
138: * @return the integer as a BigInteger.
139: */
140: BigInteger getBigInteger(int len) throws IOException {
141: if (len > available())
142: throw new IOException("short read of integer");
143:
144: if (len == 0) {
145: throw new IOException(
146: "Invalid encoding: zero length Int value");
147: }
148:
149: byte[] bytes = new byte[len];
150:
151: System.arraycopy(buf, pos, bytes, 0, len);
152: skip(len);
153:
154: return new BigInteger(bytes);
155: }
156:
157: /**
158: * Returns the integer which takes up the specified number
159: * of bytes in this buffer.
160: * @throws IOException if the result is not within the valid
161: * range for integer, i.e. between Integer.MIN_VALUE and
162: * Integer.MAX_VALUE.
163: * @param len the number of bytes to use.
164: * @return the integer.
165: */
166: public int getInteger(int len) throws IOException {
167:
168: BigInteger result = getBigInteger(len);
169: if (result.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) < 0) {
170: throw new IOException("Integer below minimum valid value");
171: }
172: if (result.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) > 0) {
173: throw new IOException("Integer exceeds maximum valid value");
174: }
175: return result.intValue();
176: }
177:
178: /**
179: * Returns the bit string which takes up the specified
180: * number of bytes in this buffer.
181: */
182: public byte[] getBitString(int len) throws IOException {
183: if (len > available())
184: throw new IOException("short read of bit string");
185:
186: if (len == 0) {
187: throw new IOException(
188: "Invalid encoding: zero length bit string");
189: }
190:
191: int numOfPadBits = buf[pos];
192: if ((numOfPadBits < 0) || (numOfPadBits > 7)) {
193: throw new IOException("Invalid number of padding bits");
194: }
195: // minus the first byte which indicates the number of padding bits
196: byte[] retval = new byte[len - 1];
197: System.arraycopy(buf, pos + 1, retval, 0, len - 1);
198: if (numOfPadBits != 0) {
199: // get rid of the padding bits
200: retval[len - 2] &= (0xff << numOfPadBits);
201: }
202: skip(len);
203: return retval;
204: }
205:
206: /**
207: * Returns the bit string which takes up the rest of this buffer.
208: */
209: byte[] getBitString() throws IOException {
210: return getBitString(available());
211: }
212:
213: /**
214: * Returns the bit string which takes up the rest of this buffer.
215: * The bit string need not be byte-aligned.
216: */
217: BitArray getUnalignedBitString() throws IOException {
218: if (pos >= count)
219: return null;
220: /*
221: * Just copy the data into an aligned, padded octet buffer,
222: * and consume the rest of the buffer.
223: */
224: int len = available();
225: int unusedBits = buf[pos] & 0xff;
226: if (unusedBits > 7) {
227: throw new IOException("Invalid value for unused bits: "
228: + unusedBits);
229: }
230: byte[] bits = new byte[len - 1];
231: // number of valid bits
232: int length = (bits.length == 0) ? 0 : bits.length * 8
233: - unusedBits;
234:
235: System.arraycopy(buf, pos + 1, bits, 0, len - 1);
236:
237: BitArray bitArray = new BitArray(length, bits);
238: pos = count;
239: return bitArray;
240: }
241:
242: /**
243: * Returns the UTC Time value that takes up the specified number
244: * of bytes in this buffer.
245: * @param len the number of bytes to use
246: */
247: public Date getUTCTime(int len) throws IOException {
248: if (len > available())
249: throw new IOException("short read of DER UTC Time");
250:
251: if (len < 11 || len > 17)
252: throw new IOException("DER UTC Time length error");
253:
254: return getTime(len, false);
255: }
256:
257: /**
258: * Returns the Generalized Time value that takes up the specified
259: * number of bytes in this buffer.
260: * @param len the number of bytes to use
261: */
262: public Date getGeneralizedTime(int len) throws IOException {
263: if (len > available())
264: throw new IOException("short read of DER Generalized Time");
265:
266: if (len < 13 || len > 19)
267: throw new IOException("DER Generalized Time length error");
268:
269: return getTime(len, true);
270:
271: }
272:
273: /**
274: * Private helper routine to extract time from the der value.
275: * @param len the number of bytes to use
276: * @param generalized true if Generalized Time is to be read, false
277: * if UTC Time is to be read.
278: */
279: private Date getTime(int len, boolean generalized)
280: throws IOException {
281:
282: /*
283: * UTC time encoded as ASCII chars:
284: * YYMMDDhhmmZ
285: * YYMMDDhhmmssZ
286: * YYMMDDhhmm+hhmm
287: * YYMMDDhhmm-hhmm
288: * YYMMDDhhmmss+hhmm
289: * YYMMDDhhmmss-hhmm
290: * UTC Time is broken in storing only two digits of year.
291: * If YY < 50, we assume 20YY;
292: * if YY >= 50, we assume 19YY, as per RFC 2459.
293: *
294: * Generalized time has a four-digit year and allows any
295: * precision specified in ISO 8601. However, for our purposes,
296: * we will only allow the same format as UTC time.
297: */
298:
299: int year, month, day, hour, minute, second;
300: String type = null;
301:
302: if (generalized) {
303: type = "Generalized";
304: year = 1000 * Character.digit((char) buf[pos++], 10);
305: year += 100 * Character.digit((char) buf[pos++], 10);
306: year += 10 * Character.digit((char) buf[pos++], 10);
307: year += Character.digit((char) buf[pos++], 10);
308: len -= 2; // For the two extra YY
309: } else {
310: type = "UTC";
311: year = 10 * Character.digit((char) buf[pos++], 10);
312: year += Character.digit((char) buf[pos++], 10);
313:
314: if (year < 50) // origin 2000
315: year += 2000;
316: else
317: year += 1900; // origin 1900
318: }
319:
320: month = 10 * Character.digit((char) buf[pos++], 10);
321: month += Character.digit((char) buf[pos++], 10);
322:
323: day = 10 * Character.digit((char) buf[pos++], 10);
324: day += Character.digit((char) buf[pos++], 10);
325:
326: hour = 10 * Character.digit((char) buf[pos++], 10);
327: hour += Character.digit((char) buf[pos++], 10);
328:
329: minute = 10 * Character.digit((char) buf[pos++], 10);
330: minute += Character.digit((char) buf[pos++], 10);
331:
332: len -= 10; // YYMMDDhhmm
333:
334: /*
335: * We allow for non-encoded seconds, even though the
336: * IETF-PKIX specification says that the seconds should
337: * always be encoded even if it is zero.
338: */
339:
340: if (len == 3 || len == 7) {
341: second = 10 * Character.digit((char) buf[pos++], 10);
342: second += Character.digit((char) buf[pos++], 10);
343: len -= 2;
344: } else
345: second = 0;
346:
347: if (month == 0 || day == 0 || month > 12 || day > 31
348: || hour >= 24 || minute >= 60 || second >= 60)
349: throw new IOException("Parse " + type
350: + " time, invalid format");
351:
352: CalendarDate date = new CalendarDate(year, month - 1, day);
353: date
354: .setTimeOfDay(((((hour * 60) + minute) * 60) + second) * 1000);
355: /*
356: * Generalized time can theoretically allow any precision,
357: * but we're not supporting that.
358: */
359: long time = Gregorian.dateToMillis(date);
360:
361: /*
362: * Finally, "Z" or "+hhmm" or "-hhmm" ... offsets change hhmm
363: */
364: if (!(len == 1 || len == 5))
365: throw new IOException("Parse " + type
366: + " time, invalid offset");
367:
368: int hr, min;
369:
370: switch (buf[pos++]) {
371: case '+':
372: hr = 10 * Character.digit((char) buf[pos++], 10);
373: hr += Character.digit((char) buf[pos++], 10);
374: min = 10 * Character.digit((char) buf[pos++], 10);
375: min += Character.digit((char) buf[pos++], 10);
376:
377: if (hr >= 24 || min >= 60)
378: throw new IOException("Parse " + type + " time, +hhmm");
379:
380: time -= ((hr * 60) + min) * 60 * 1000;
381: break;
382:
383: case '-':
384: hr = 10 * Character.digit((char) buf[pos++], 10);
385: hr += Character.digit((char) buf[pos++], 10);
386: min = 10 * Character.digit((char) buf[pos++], 10);
387: min += Character.digit((char) buf[pos++], 10);
388:
389: if (hr >= 24 || min >= 60)
390: throw new IOException("Parse " + type + " time, -hhmm");
391:
392: time += ((hr * 60) + min) * 60 * 1000;
393: break;
394:
395: case 'Z':
396: break;
397:
398: default:
399: throw new IOException("Parse " + type
400: + " time, garbage offset");
401: }
402: return new Date(time);
403: }
404: }
|