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