Dead
[official-gcc.git] / gomp-20050608-branch / libjava / classpath / gnu / java / security / der / DERReader.java
blob09ec1e2dff0b08e6af8b767edb7fb21a1552d839
1 /* DERReader.java -- parses ASN.1 DER sequences
2 Copyright (C) 2003 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.security.der;
41 import gnu.java.security.OID;
43 import java.io.BufferedInputStream;
44 import java.io.ByteArrayInputStream;
45 import java.io.ByteArrayOutputStream;
46 import java.io.EOFException;
47 import java.io.IOException;
48 import java.io.InputStream;
49 import java.math.BigInteger;
50 import java.util.Calendar;
51 import java.util.Date;
52 import java.util.TimeZone;
54 /**
55 * This class decodes DER sequences into Java objects. The methods of
56 * this class do not have knowledge of higher-levels of structure in the
57 * DER stream -- such as ASN.1 constructions -- and it is therefore up
58 * to the calling application to determine if the data are structured
59 * properly by inspecting the {@link DERValue} that is returned.
61 * @author Casey Marshall (csm@gnu.org)
63 public class DERReader implements DER
66 // Fields.
67 // ------------------------------------------------------------------------
69 protected InputStream in;
71 protected final ByteArrayOutputStream encBuf;
73 // Constructor.
74 // ------------------------------------------------------------------------
76 /**
77 * Create a new DER reader from a byte array.
79 * @param in The encoded bytes.
81 public DERReader(byte[] in)
83 this(new ByteArrayInputStream(in));
86 public DERReader (byte[] in, int off, int len)
88 this (new ByteArrayInputStream (in, off, len));
91 /**
92 * Create a new DER readed from an input stream.
94 * @param in The encoded bytes.
96 public DERReader(InputStream in)
98 if (!in.markSupported())
99 this.in = new BufferedInputStream(in, 16384);
100 else
101 this.in = in;
102 encBuf = new ByteArrayOutputStream(2048);
105 // Class methods.
106 // ------------------------------------------------------------------------
109 * Convenience method for reading a single primitive value from the
110 * given byte array.
112 * @param encoded The encoded bytes.
113 * @throws IOException If the bytes do not represent an encoded
114 * object.
116 public static DERValue read(byte[] encoded) throws IOException
118 return new DERReader(encoded).read();
121 // Instance methods.
122 // ------------------------------------------------------------------------
124 public void skip (int bytes) throws IOException
126 in.skip (bytes);
130 * Decode a single value from the input stream, returning it in a new
131 * {@link DERValue}. By "single value" we mean any single type in its
132 * entirety -- including constructed types such as SEQUENCE and all
133 * the values they contain. Usually it is sufficient to call this
134 * method once to parse and return the top-level structure, then to
135 * inspect the returned value for the proper contents.
137 * @return The parsed DER structure.
138 * @throws IOException If an error occurs reading from the input
139 * stream.
140 * @throws DEREncodingException If the input does not represent a
141 * valid DER stream.
143 public DERValue read() throws IOException
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)
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;
162 switch (tag & 0xC0)
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 // This should not be reached, since (I think) APPLICATION is
178 // always constructed.
179 throw new DEREncodingException("non-constructed APPLICATION data");
180 default:
181 throw new DEREncodingException("PRIVATE class not supported");
183 return value;
186 protected int readLength() throws IOException
188 int i = in.read();
189 if (i == -1)
190 throw new EOFException();
191 encBuf.write(i);
192 if ((i & ~0x7F) == 0)
194 return i;
196 else if (i < 0xFF)
198 byte[] octets = new byte[i & 0x7F];
199 in.read(octets);
200 encBuf.write(octets);
201 return new BigInteger(1, octets).intValue();
203 throw new DEREncodingException();
206 // Own methods.
207 // ------------------------------------------------------------------------
209 private Object readUniversal(int tag, int len) throws IOException
211 byte[] value = new byte[len];
212 in.read(value);
213 encBuf.write(value);
214 switch (tag & 0x1F)
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);
257 private static String makeString(int tag, byte[] value)
258 throws IOException
260 switch (tag & 0x1F)
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);
272 case UNIVERSAL_STRING:
273 // XXX The docs say UniversalString is encoded in four bytes
274 // per character, but Java has no support (yet) for UTF-32.
275 //return new String(buf, "UTF-32");
276 case BMP_STRING:
277 return fromUtf16Be(value);
279 case UTF8_STRING:
280 return fromUtf8(value);
282 default:
283 throw new DEREncodingException("unknown string tag");
287 private static String fromIso88591(byte[] bytes)
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();
295 private static String fromUtf16Be(byte[] bytes) throws IOException
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)
302 char c = (char) ((bytes[i] << 8) & 0xFF);
303 c |= (char) (bytes[i+1] & 0xFF);
304 str.append(c);
306 return str.toString();
309 private static String fromUtf8(byte[] bytes) throws IOException
311 StringBuffer str = new StringBuffer((int)(bytes.length / 1.5));
312 for (int i = 0; i < bytes.length; )
314 char c = 0;
315 if ((bytes[i] & 0xE0) == 0xE0)
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);
327 else if ((bytes[i] & 0xC0) == 0xC0)
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);
336 else if ((bytes[i] & 0xFF) < 0x80)
338 c = (char) (bytes[i++] & 0xFF);
340 else
341 throw new IOException("badly formed UTF-8 sequence");
342 str.append(c);
344 return str.toString();
347 private Date makeTime(int tag, byte[] value) throws IOException
349 Calendar calendar = Calendar.getInstance();
350 String str = makeString(PRINTABLE_STRING, value);
352 // Classpath's SimpleDateFormat does not work for parsing these
353 // types of times, so we do this by hand.
354 String date = str;
355 String tz = "";
356 if (str.indexOf("+") > 0)
358 date = str.substring(0, str.indexOf("+"));
359 tz = str.substring(str.indexOf("+"));
361 else if (str.indexOf("-") > 0)
363 date = str.substring(0, str.indexOf("-"));
364 tz = str.substring(str.indexOf("-"));
366 else if (str.endsWith("Z"))
368 date = str.substring(0, str.length()-2);
369 tz = "Z";
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)
377 if (date.length() < 10) // must be at least 10 chars long
378 throw new DEREncodingException("cannot parse date");
379 // UTCTime is of the form "yyMMddHHmm[ss](Z|(+|-)hhmm)"
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, // month
389 Integer.parseInt(str.substring( 4, 6)), // day
390 Integer.parseInt(str.substring( 6, 8)), // hour
391 Integer.parseInt(str.substring( 8, 10))); // minute
392 if (date.length() == 12)
393 calendar.set(Calendar.SECOND,
394 Integer.parseInt(str.substring(10, 12)));
396 catch (NumberFormatException nfe)
398 throw new DEREncodingException("cannot parse date");
401 else
403 if (date.length() < 10) // must be at least 10 chars long
404 throw new DEREncodingException("cannot parse date");
405 // GeneralTime is of the form "yyyyMMddHH[mm[ss[(.|,)SSSS]]]"
406 // followed by "Z" or "(+|-)hh[mm]"
409 calendar.set(
410 Integer.parseInt(date.substring(0, 4)), // year
411 Integer.parseInt(date.substring(4, 6))-1, // month
412 Integer.parseInt(date.substring(6, 8)), // day
413 Integer.parseInt(date.substring(8, 10)), 0); // hour, min
414 switch (date.length())
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)));
430 catch (NumberFormatException nfe)
432 throw new DEREncodingException("cannot parse date");
435 return calendar.getTime();