Merge from mainline (gomp-merge-2005-02-26).
[official-gcc.git] / libjava / gnu / java / security / der / DERReader.java
blob688b509eb2ce6ac65d1ba8f9e6b0e950d8debdfc
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., 59 Temple Place, Suite 330, Boston, MA
19 02111-1307 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 java.io.BufferedInputStream;
42 import java.io.ByteArrayInputStream;
43 import java.io.ByteArrayOutputStream;
44 import java.io.EOFException;
45 import java.io.InputStream;
46 import java.io.IOException;
48 import java.math.BigInteger;
50 import java.util.Calendar;
51 import java.util.Date;
52 import java.util.TimeZone;
54 import gnu.java.security.OID;
56 /**
57 * This class decodes DER sequences into Java objects. The methods of
58 * this class do not have knowledge of higher-levels of structure in the
59 * DER stream -- such as ASN.1 constructions -- and it is therefore up
60 * to the calling application to determine if the data are structured
61 * properly by inspecting the {@link DERValue} that is returned.
63 * @author Casey Marshall (csm@gnu.org)
65 public class DERReader implements DER
68 // Fields.
69 // ------------------------------------------------------------------------
71 protected InputStream in;
73 protected final ByteArrayOutputStream encBuf;
75 // Constructor.
76 // ------------------------------------------------------------------------
78 /**
79 * Create a new DER reader from a byte array.
81 * @param in The encoded bytes.
83 public DERReader(byte[] in)
85 this(new ByteArrayInputStream(in));
88 public DERReader (byte[] in, int off, int len)
90 this (new ByteArrayInputStream (in, off, len));
93 /**
94 * Create a new DER readed from an input stream.
96 * @param in The encoded bytes.
98 public DERReader(InputStream in)
100 if (!in.markSupported())
101 this.in = new BufferedInputStream(in, 16384);
102 else
103 this.in = in;
104 encBuf = new ByteArrayOutputStream(2048);
107 // Class methods.
108 // ------------------------------------------------------------------------
111 * Convenience method for reading a single primitive value from the
112 * given byte array.
114 * @param encoded The encoded bytes.
115 * @throws IOException If the bytes do not represent an encoded
116 * object.
118 public static DERValue read(byte[] encoded) throws IOException
120 return new DERReader(encoded).read();
123 // Instance methods.
124 // ------------------------------------------------------------------------
126 public void skip (int bytes) throws IOException
128 in.skip (bytes);
132 * Decode a single value from the input stream, returning it in a new
133 * {@link DERValue}. By "single value" we mean any single type in its
134 * entirety -- including constructed types such as SEQUENCE and all
135 * the values they contain. Usually it is sufficient to call this
136 * method once to parse and return the top-level structure, then to
137 * inspect the returned value for the proper contents.
139 * @return The parsed DER structure.
140 * @throws IOException If an error occurs reading from the input
141 * stream.
142 * @throws DEREncodingException If the input does not represent a
143 * valid DER stream.
145 public DERValue read() throws IOException
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)
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;
164 switch (tag & 0xC0)
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 // This should not be reached, since (I think) APPLICATION is
180 // always constructed.
181 throw new DEREncodingException("non-constructed APPLICATION data");
182 default:
183 throw new DEREncodingException("PRIVATE class not supported");
185 return value;
188 protected int readLength() throws IOException
190 int i = in.read();
191 if (i == -1)
192 throw new EOFException();
193 encBuf.write(i);
194 if ((i & ~0x7F) == 0)
196 return i;
198 else if (i < 0xFF)
200 byte[] octets = new byte[i & 0x7F];
201 in.read(octets);
202 encBuf.write(octets);
203 return new BigInteger(1, octets).intValue();
205 throw new DEREncodingException();
208 // Own methods.
209 // ------------------------------------------------------------------------
211 private Object readUniversal(int tag, int len) throws IOException
213 byte[] value = new byte[len];
214 in.read(value);
215 encBuf.write(value);
216 switch (tag & 0x1F)
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);
259 private static String makeString(int tag, byte[] value)
260 throws IOException
262 switch (tag & 0x1F)
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);
274 case UNIVERSAL_STRING:
275 // XXX The docs say UniversalString is encoded in four bytes
276 // per character, but Java has no support (yet) for UTF-32.
277 //return new String(buf, "UTF-32");
278 case BMP_STRING:
279 return fromUtf16Be(value);
281 case UTF8_STRING:
282 return fromUtf8(value);
284 default:
285 throw new DEREncodingException("unknown string tag");
289 private static String fromIso88591(byte[] bytes)
291 StringBuffer str = new StringBuffer(bytes.length);
292 for (int i = 0; i < bytes.length; i++)
293 str.append((char) (bytes[i] & 0xFF));
294 return str.toString();
297 private static String fromUtf16Be(byte[] bytes) throws IOException
299 if ((bytes.length & 0x01) != 0)
300 throw new IOException("UTF-16 bytes are odd in length");
301 StringBuffer str = new StringBuffer(bytes.length / 2);
302 for (int i = 0; i < bytes.length; i += 2)
304 char c = (char) ((bytes[i] << 8) & 0xFF);
305 c |= (char) (bytes[i+1] & 0xFF);
306 str.append(c);
308 return str.toString();
311 private static String fromUtf8(byte[] bytes) throws IOException
313 StringBuffer str = new StringBuffer((int)(bytes.length / 1.5));
314 for (int i = 0; i < bytes.length; )
316 char c = 0;
317 if ((bytes[i] & 0xE0) == 0xE0)
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);
329 else if ((bytes[i] & 0xC0) == 0xC0)
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);
338 else if ((bytes[i] & 0xFF) < 0x80)
340 c = (char) (bytes[i++] & 0xFF);
342 else
343 throw new IOException("badly formed UTF-8 sequence");
344 str.append(c);
346 return str.toString();
349 private Date makeTime(int tag, byte[] value) throws IOException
351 Calendar calendar = Calendar.getInstance();
352 String str = makeString(PRINTABLE_STRING, value);
354 // Classpath's SimpleDateFormat does not work for parsing these
355 // types of times, so we do this by hand.
356 String date = str;
357 String tz = "";
358 if (str.indexOf("+") > 0)
360 date = str.substring(0, str.indexOf("+"));
361 tz = str.substring(str.indexOf("+"));
363 else if (str.indexOf("-") > 0)
365 date = str.substring(0, str.indexOf("-"));
366 tz = str.substring(str.indexOf("-"));
368 else if (str.endsWith("Z"))
370 date = str.substring(0, str.length()-2);
371 tz = "Z";
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)
379 if (date.length() < 10) // must be at least 10 chars long
380 throw new DEREncodingException("cannot parse date");
381 // UTCTime is of the form "yyMMddHHmm[ss](Z|(+|-)hhmm)"
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, // month
391 Integer.parseInt(str.substring( 4, 6)), // day
392 Integer.parseInt(str.substring( 6, 8)), // hour
393 Integer.parseInt(str.substring( 8, 10))); // minute
394 if (date.length() == 12);
395 calendar.set(Calendar.SECOND,
396 Integer.parseInt(str.substring(10, 12)));
398 catch (NumberFormatException nfe)
400 throw new DEREncodingException("cannot parse date");
403 else
405 if (date.length() < 10) // must be at least 10 chars long
406 throw new DEREncodingException("cannot parse date");
407 // GeneralTime is of the form "yyyyMMddHH[mm[ss[(.|,)SSSS]]]"
408 // followed by "Z" or "(+|-)hh[mm]"
411 calendar.set(
412 Integer.parseInt(date.substring(0, 4)), // year
413 Integer.parseInt(date.substring(4, 6))-1, // month
414 Integer.parseInt(date.substring(6, 8)), // day
415 Integer.parseInt(date.substring(8, 10)), 0); // hour, min
416 switch (date.length())
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)));
432 catch (NumberFormatException nfe)
434 throw new DEREncodingException("cannot parse date");
437 return calendar.getTime();