1 /**
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one or more
4 * contributor license agreements. See the NOTICE file distributed with
5 * this work for additional information regarding copyright ownership.
6 * The ASF licenses this file to You under the Apache License, Version 2.0
7 * (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 package org.apache.geronimo.util.asn1;
20
21 import java.io.ByteArrayInputStream;
22 import java.io.ByteArrayOutputStream;
23 import java.io.EOFException;
24 import java.io.FilterInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.util.Vector;
28
29 /**
30 * a general purpose ASN.1 decoder - note: this class differs from the
31 * others in that it returns null after it has read the last object in
32 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
33 * returned.
34 */
35 public class ASN1InputStream
36 extends FilterInputStream
37 implements DERTags
38 {
39 private DERObject END_OF_STREAM = new DERObject() {
40 void encode(
41 DEROutputStream out)
42 throws IOException
43 {
44 throw new IOException("Eeek!");
45 }
46 public int hashCode()
47 {
48 return 0;
49 }
50 public boolean equals(
51 Object o)
52 {
53 return o == this;
54 }
55 };
56 boolean eofFound = false;
57
58 public ASN1InputStream(
59 InputStream is)
60 {
61 super(is);
62 }
63
64 public ASN1InputStream(
65 byte[] input)
66 {
67 super(new ByteArrayInputStream(input));
68 }
69
70 protected int readLength()
71 throws IOException
72 {
73 int length = read();
74 if (length < 0)
75 {
76 throw new IOException("EOF found when length expected");
77 }
78
79 if (length == 0x80)
80 {
81 return -1;
82 }
83
84 if (length > 127)
85 {
86 int size = length & 0x7f;
87
88 if (size > 4)
89 {
90 throw new IOException("DER length more than 4 bytes");
91 }
92
93 length = 0;
94 for (int i = 0; i < size; i++)
95 {
96 int next = read();
97
98 if (next < 0)
99 {
100 throw new IOException("EOF found reading length");
101 }
102
103 length = (length << 8) + next;
104 }
105
106 if (length < 0)
107 {
108 throw new IOException("corrupted steam - negative length found");
109 }
110 }
111
112 return length;
113 }
114
115 protected void readFully(
116 byte[] bytes)
117 throws IOException
118 {
119 int left = bytes.length;
120 int len;
121
122 if (left == 0)
123 {
124 return;
125 }
126
127 while ((len = read(bytes, bytes.length - left, left)) > 0)
128 {
129 if ((left -= len) == 0)
130 {
131 return;
132 }
133 }
134
135 if (left != 0)
136 {
137 throw new EOFException("EOF encountered in middle of object");
138 }
139 }
140
141 /**
142 * build an object given its tag and a byte stream to construct it
143 * from.
144 */
145 protected DERObject buildObject(
146 int tag,
147 byte[] bytes)
148 throws IOException
149 {
150 if ((tag & APPLICATION) != 0)
151 {
152 return new DERApplicationSpecific(tag, bytes);
153 }
154
155 switch (tag)
156 {
157 case NULL:
158 return new DERNull();
159 case SEQUENCE | CONSTRUCTED:
160 ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
161 ASN1InputStream aIn = new ASN1InputStream(bIn);
162 ASN1EncodableVector v = new ASN1EncodableVector();
163
164 DERObject obj = aIn.readObject();
165
166 while (obj != null)
167 {
168 v.add(obj);
169 obj = aIn.readObject();
170 }
171
172 return new DERSequence(v);
173 case SET | CONSTRUCTED:
174 bIn = new ByteArrayInputStream(bytes);
175 aIn = new ASN1InputStream(bIn);
176 v = new ASN1EncodableVector();
177
178 obj = aIn.readObject();
179
180 while (obj != null)
181 {
182 v.add(obj);
183 obj = aIn.readObject();
184 }
185
186 return new DERSet(v, false);
187 case BOOLEAN:
188 return new DERBoolean(bytes);
189 case INTEGER:
190 return new DERInteger(bytes);
191 case ENUMERATED:
192 return new DEREnumerated(bytes);
193 case OBJECT_IDENTIFIER:
194 return new DERObjectIdentifier(bytes);
195 case BIT_STRING:
196 int padBits = bytes[0];
197 byte[] data = new byte[bytes.length - 1];
198
199 System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
200
201 return new DERBitString(data, padBits);
202 case NUMERIC_STRING:
203 return new DERNumericString(bytes);
204 case UTF8_STRING:
205 return new DERUTF8String(bytes);
206 case PRINTABLE_STRING:
207 return new DERPrintableString(bytes);
208 case IA5_STRING:
209 return new DERIA5String(bytes);
210 case T61_STRING:
211 return new DERT61String(bytes);
212 case VISIBLE_STRING:
213 return new DERVisibleString(bytes);
214 case GENERAL_STRING:
215 return new DERGeneralString(bytes);
216 case UNIVERSAL_STRING:
217 return new DERUniversalString(bytes);
218 case BMP_STRING:
219 return new DERBMPString(bytes);
220 case OCTET_STRING:
221 return new DEROctetString(bytes);
222 case UTC_TIME:
223 return new DERUTCTime(bytes);
224 case GENERALIZED_TIME:
225 return new DERGeneralizedTime(bytes);
226 default:
227
228
229
230 if ((tag & TAGGED) != 0)
231 {
232 int tagNo = tag & 0x1f;
233
234 if (tagNo == 0x1f)
235 {
236 int idx = 0;
237
238 tagNo = 0;
239
240 while ((bytes[idx] & 0x80) != 0)
241 {
242 tagNo |= (bytes[idx++] & 0x7f);
243 tagNo <<= 7;
244 }
245
246 tagNo |= (bytes[idx] & 0x7f);
247
248 byte[] tmp = bytes;
249
250 bytes = new byte[tmp.length - (idx + 1)];
251 System.arraycopy(tmp, idx + 1, bytes, 0, bytes.length);
252 }
253
254 if (bytes.length == 0)
255 {
256 if ((tag & CONSTRUCTED) == 0)
257 {
258 return new DERTaggedObject(false, tagNo, new DERNull());
259 }
260 else
261 {
262 return new DERTaggedObject(false, tagNo, new DERSequence());
263 }
264 }
265
266
267
268
269 if ((tag & CONSTRUCTED) == 0)
270 {
271 return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
272 }
273
274 bIn = new ByteArrayInputStream(bytes);
275 aIn = new ASN1InputStream(bIn);
276
277 DEREncodable dObj = aIn.readObject();
278
279
280
281
282
283 if (aIn.available() == 0)
284 {
285 return new DERTaggedObject(tagNo, dObj);
286 }
287
288
289
290
291 v = new ASN1EncodableVector();
292
293 while (dObj != null)
294 {
295 v.add(dObj);
296 dObj = aIn.readObject();
297 }
298
299 return new DERTaggedObject(false, tagNo, new DERSequence(v));
300 }
301
302 return new DERUnknownTag(tag, bytes);
303 }
304 }
305
306 /**
307 * read a string of bytes representing an indefinite length object.
308 */
309 private byte[] readIndefiniteLengthFully()
310 throws IOException
311 {
312 ByteArrayOutputStream bOut = new ByteArrayOutputStream();
313 int b, b1;
314
315 b1 = read();
316
317 while ((b = read()) >= 0)
318 {
319 if (b1 == 0 && b == 0)
320 {
321 break;
322 }
323
324 bOut.write(b1);
325 b1 = b;
326 }
327
328 return bOut.toByteArray();
329 }
330
331 private BERConstructedOctetString buildConstructedOctetString()
332 throws IOException
333 {
334 Vector octs = new Vector();
335
336 for (;;)
337 {
338 DERObject o = readObject();
339
340 if (o == END_OF_STREAM)
341 {
342 break;
343 }
344
345 octs.addElement(o);
346 }
347
348 return new BERConstructedOctetString(octs);
349 }
350
351 public DERObject readObject()
352 throws IOException
353 {
354 int tag = read();
355 if (tag == -1)
356 {
357 if (eofFound)
358 {
359 throw new EOFException("attempt to read past end of file.");
360 }
361
362 eofFound = true;
363
364 return null;
365 }
366
367 int length = readLength();
368
369 if (length < 0)
370 {
371 switch (tag)
372 {
373 case NULL:
374 return new BERNull();
375 case SEQUENCE | CONSTRUCTED:
376 ASN1EncodableVector v = new ASN1EncodableVector();
377
378 for (;;)
379 {
380 DERObject obj = readObject();
381
382 if (obj == END_OF_STREAM)
383 {
384 break;
385 }
386
387 v.add(obj);
388 }
389 return new BERSequence(v);
390 case SET | CONSTRUCTED:
391 v = new ASN1EncodableVector();
392
393 for (;;)
394 {
395 DERObject obj = readObject();
396
397 if (obj == END_OF_STREAM)
398 {
399 break;
400 }
401
402 v.add(obj);
403 }
404 return new BERSet(v, false);
405 case OCTET_STRING | CONSTRUCTED:
406 return buildConstructedOctetString();
407 default:
408
409
410
411 if ((tag & TAGGED) != 0)
412 {
413 int tagNo = tag & 0x1f;
414
415 if (tagNo == 0x1f)
416 {
417 int b = read();
418
419 tagNo = 0;
420
421 while ((b >= 0) && ((b & 0x80) != 0))
422 {
423 tagNo |= (b & 0x7f);
424 tagNo <<= 7;
425 b = read();
426 }
427
428 tagNo |= (b & 0x7f);
429 }
430
431
432
433
434 if ((tag & CONSTRUCTED) == 0)
435 {
436 byte[] bytes = readIndefiniteLengthFully();
437
438 return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
439 }
440
441
442
443
444 DERObject dObj = readObject();
445
446 if (dObj == END_OF_STREAM)
447 {
448 return new DERTaggedObject(tagNo);
449 }
450
451 DERObject next = readObject();
452
453
454
455
456
457 if (next == END_OF_STREAM)
458 {
459 return new BERTaggedObject(tagNo, dObj);
460 }
461
462
463
464
465 v = new ASN1EncodableVector();
466
467 v.add(dObj);
468
469 do
470 {
471 v.add(next);
472 next = readObject();
473 }
474 while (next != END_OF_STREAM);
475
476 return new BERTaggedObject(false, tagNo, new BERSequence(v));
477 }
478
479 throw new IOException("unknown BER object encountered");
480 }
481 }
482 else
483 {
484 if (tag == 0 && length == 0)
485 {
486 return END_OF_STREAM;
487 }
488
489 byte[] bytes = new byte[length];
490
491 readFully(bytes);
492
493 return buildObject(tag, bytes);
494 }
495 }
496 }