1:
37:
38:
39: package ;
40:
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53:
54:
63: public class DERReader implements DER
64: {
65:
66:
67:
68:
69: protected InputStream in;
70:
71: protected final ByteArrayOutputStream encBuf;
72:
73:
74:
75:
76:
81: public DERReader(byte[] in)
82: {
83: this(new ByteArrayInputStream(in));
84: }
85:
86: public DERReader (byte[] in, int off, int len)
87: {
88: this (new ByteArrayInputStream (in, off, len));
89: }
90:
91:
96: public DERReader(InputStream in)
97: {
98: if (!in.markSupported())
99: this.in = new BufferedInputStream(in, 16384);
100: else
101: this.in = in;
102: encBuf = new ByteArrayOutputStream(2048);
103: }
104:
105:
106:
107:
108:
116: public static DERValue read(byte[] encoded) throws IOException
117: {
118: return new DERReader(encoded).read();
119: }
120:
121:
122:
123:
124: public void skip (int bytes) throws IOException
125: {
126: in.skip (bytes);
127: }
128:
129:
143: public DERValue read() throws IOException
144: {
145: int tag = in.read();
146: if (tag == -1)
147: throw new EOFException();
148: encBuf.write(tag);
149: int len = readLength();
150: DERValue value = null;
151: if ((tag & CONSTRUCTED) == CONSTRUCTED)
152: {
153: in.mark(2048);
154: byte[] encoded = new byte[len];
155: in.read(encoded);
156: encBuf.write(encoded);
157: value = new DERValue(tag, len, CONSTRUCTED_VALUE, encBuf.toByteArray());
158: in.reset();
159: encBuf.reset();
160: return value;
161: }
162: switch (tag & 0xC0)
163: {
164: case UNIVERSAL:
165: value = new DERValue(tag, len, readUniversal(tag, len),
166: encBuf.toByteArray());
167: encBuf.reset();
168: break;
169: case CONTEXT:
170: byte[] encoded = new byte[len];
171: in.read(encoded);
172: encBuf.write(encoded);
173: value = new DERValue(tag, len, encoded, encBuf.toByteArray());
174: encBuf.reset();
175: break;
176: case APPLICATION:
177:
178:
179: throw new DEREncodingException("non-constructed APPLICATION data");
180: default:
181: throw new DEREncodingException("PRIVATE class not supported");
182: }
183: return value;
184: }
185:
186: protected int readLength() throws IOException
187: {
188: int i = in.read();
189: if (i == -1)
190: throw new EOFException();
191: encBuf.write(i);
192: if ((i & ~0x7F) == 0)
193: {
194: return i;
195: }
196: else if (i < 0xFF)
197: {
198: byte[] octets = new byte[i & 0x7F];
199: in.read(octets);
200: encBuf.write(octets);
201: return new BigInteger(1, octets).intValue();
202: }
203: throw new DEREncodingException();
204: }
205:
206:
207:
208:
209: private Object readUniversal(int tag, int len) throws IOException
210: {
211: byte[] value = new byte[len];
212: in.read(value);
213: encBuf.write(value);
214: switch (tag & 0x1F)
215: {
216: case BOOLEAN:
217: if (value.length != 1)
218: throw new DEREncodingException();
219: return Boolean.valueOf(value[0] != 0);
220: case NULL:
221: if (len != 0)
222: throw new DEREncodingException();
223: return null;
224: case INTEGER:
225: case ENUMERATED:
226: return new BigInteger(value);
227: case BIT_STRING:
228: byte[] bits = new byte[len - 1];
229: System.arraycopy(value, 1, bits, 0, bits.length);
230: return new BitString(bits, value[0] & 0xFF);
231: case OCTET_STRING:
232: return value;
233: case NUMERIC_STRING:
234: case PRINTABLE_STRING:
235: case T61_STRING:
236: case VIDEOTEX_STRING:
237: case IA5_STRING:
238: case GRAPHIC_STRING:
239: case ISO646_STRING:
240: case GENERAL_STRING:
241: case UNIVERSAL_STRING:
242: case BMP_STRING:
243: case UTF8_STRING:
244: return makeString(tag, value);
245: case UTC_TIME:
246: case GENERALIZED_TIME:
247: return makeTime(tag, value);
248: case OBJECT_IDENTIFIER:
249: return new OID(value);
250: case RELATIVE_OID:
251: return new OID(value, true);
252: default:
253: throw new DEREncodingException("unknown tag " + tag);
254: }
255: }
256:
257: private static String makeString(int tag, byte[] value)
258: throws IOException
259: {
260: switch (tag & 0x1F)
261: {
262: case NUMERIC_STRING:
263: case PRINTABLE_STRING:
264: case T61_STRING:
265: case VIDEOTEX_STRING:
266: case IA5_STRING:
267: case GRAPHIC_STRING:
268: case ISO646_STRING:
269: case GENERAL_STRING:
270: return fromIso88591(value);
271:
272: case UNIVERSAL_STRING:
273:
274:
275:
276: case BMP_STRING:
277: return fromUtf16Be(value);
278:
279: case UTF8_STRING:
280: return fromUtf8(value);
281:
282: default:
283: throw new DEREncodingException("unknown string tag");
284: }
285: }
286:
287: private static String fromIso88591(byte[] bytes)
288: {
289: StringBuffer str = new StringBuffer(bytes.length);
290: for (int i = 0; i < bytes.length; i++)
291: str.append((char) (bytes[i] & 0xFF));
292: return str.toString();
293: }
294:
295: private static String fromUtf16Be(byte[] bytes) throws IOException
296: {
297: if ((bytes.length & 0x01) != 0)
298: throw new IOException("UTF-16 bytes are odd in length");
299: StringBuffer str = new StringBuffer(bytes.length / 2);
300: for (int i = 0; i < bytes.length; i += 2)
301: {
302: char c = (char) ((bytes[i] << 8) & 0xFF);
303: c |= (char) (bytes[i+1] & 0xFF);
304: str.append(c);
305: }
306: return str.toString();
307: }
308:
309: private static String fromUtf8(byte[] bytes) throws IOException
310: {
311: StringBuffer str = new StringBuffer((int)(bytes.length / 1.5));
312: for (int i = 0; i < bytes.length; )
313: {
314: char c = 0;
315: if ((bytes[i] & 0xE0) == 0xE0)
316: {
317: if ((i + 2) >= bytes.length)
318: throw new IOException("short UTF-8 input");
319: c = (char) ((bytes[i++] & 0x0F) << 12);
320: if ((bytes[i] & 0x80) != 0x80)
321: throw new IOException("malformed UTF-8 input");
322: c |= (char) ((bytes[i++] & 0x3F) << 6);
323: if ((bytes[i] & 0x80) != 0x80)
324: throw new IOException("malformed UTF-8 input");
325: c |= (char) (bytes[i++] & 0x3F);
326: }
327: else if ((bytes[i] & 0xC0) == 0xC0)
328: {
329: if ((i + 1) >= bytes.length)
330: throw new IOException("short input");
331: c = (char) ((bytes[i++] & 0x1F) << 6);
332: if ((bytes[i] & 0x80) != 0x80)
333: throw new IOException("malformed UTF-8 input");
334: c |= (char) (bytes[i++] & 0x3F);
335: }
336: else if ((bytes[i] & 0xFF) < 0x80)
337: {
338: c = (char) (bytes[i++] & 0xFF);
339: }
340: else
341: throw new IOException("badly formed UTF-8 sequence");
342: str.append(c);
343: }
344: return str.toString();
345: }
346:
347: private Date makeTime(int tag, byte[] value) throws IOException
348: {
349: Calendar calendar = Calendar.getInstance();
350: String str = makeString(PRINTABLE_STRING, value);
351:
352:
353:
354: String date = str;
355: String tz = "";
356: if (str.indexOf("+") > 0)
357: {
358: date = str.substring(0, str.indexOf("+"));
359: tz = str.substring(str.indexOf("+"));
360: }
361: else if (str.indexOf("-") > 0)
362: {
363: date = str.substring(0, str.indexOf("-"));
364: tz = str.substring(str.indexOf("-"));
365: }
366: else if (str.endsWith("Z"))
367: {
368: date = str.substring(0, str.length()-2);
369: tz = "Z";
370: }
371: if (!tz.equals("Z") && tz.length() > 0)
372: calendar.setTimeZone(TimeZone.getTimeZone(tz));
373: else
374: calendar.setTimeZone(TimeZone.getTimeZone("UTC"));
375: if ((tag & 0x1F) == UTC_TIME)
376: {
377: if (date.length() < 10)
378: throw new DEREncodingException("cannot parse date");
379:
380: try
381: {
382: int year = Integer.parseInt(str.substring(0, 2));
383: if (year < 50)
384: year += 2000;
385: else
386: year += 1900;
387: calendar.set(year,
388: Integer.parseInt(str.substring( 2, 4))-1,
389: Integer.parseInt(str.substring( 4, 6)),
390: Integer.parseInt(str.substring( 6, 8)),
391: Integer.parseInt(str.substring( 8, 10)));
392: if (date.length() == 12)
393: calendar.set(Calendar.SECOND,
394: Integer.parseInt(str.substring(10, 12)));
395: }
396: catch (NumberFormatException nfe)
397: {
398: throw new DEREncodingException("cannot parse date");
399: }
400: }
401: else
402: {
403: if (date.length() < 10)
404: throw new DEREncodingException("cannot parse date");
405:
406:
407: try
408: {
409: calendar.set(
410: Integer.parseInt(date.substring(0, 4)),
411: Integer.parseInt(date.substring(4, 6))-1,
412: Integer.parseInt(date.substring(6, 8)),
413: Integer.parseInt(date.substring(8, 10)), 0);
414: switch (date.length())
415: {
416: case 19:
417: case 18:
418: case 17:
419: case 16:
420: calendar.set(Calendar.MILLISECOND,
421: Integer.parseInt(date.substring(15)));
422: case 14:
423: calendar.set(Calendar.SECOND,
424: Integer.parseInt(date.substring(12, 14)));
425: case 12:
426: calendar.set(Calendar.MINUTE,
427: Integer.parseInt(date.substring(10, 12)));
428: }
429: }
430: catch (NumberFormatException nfe)
431: {
432: throw new DEREncodingException("cannot parse date");
433: }
434: }
435: return calendar.getTime();
436: }
437: }