001 /**
002 *
003 * Licensed to the Apache Software Foundation (ASF) under one or more
004 * contributor license agreements. See the NOTICE file distributed with
005 * this work for additional information regarding copyright ownership.
006 * The ASF licenses this file to You under the Apache License, Version 2.0
007 * (the "License"); you may not use this file except in compliance with
008 * the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package org.apache.geronimo.util.asn1;
020
021 import java.io.ByteArrayInputStream;
022 import java.io.ByteArrayOutputStream;
023 import java.io.EOFException;
024 import java.io.FilterInputStream;
025 import java.io.IOException;
026 import java.io.InputStream;
027 import java.util.Vector;
028
029 /**
030 * a general purpose ASN.1 decoder - note: this class differs from the
031 * others in that it returns null after it has read the last object in
032 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
033 * returned.
034 */
035 public class ASN1InputStream
036 extends FilterInputStream
037 implements DERTags
038 {
039 private DERObject END_OF_STREAM = new DERObject() {
040 void encode(
041 DEROutputStream out)
042 throws IOException
043 {
044 throw new IOException("Eeek!");
045 }
046 public int hashCode()
047 {
048 return 0;
049 }
050 public boolean equals(
051 Object o)
052 {
053 return o == this;
054 }
055 };
056 boolean eofFound = false;
057
058 public ASN1InputStream(
059 InputStream is)
060 {
061 super(is);
062 }
063
064 public ASN1InputStream(
065 byte[] input)
066 {
067 super(new ByteArrayInputStream(input));
068 }
069
070 protected int readLength()
071 throws IOException
072 {
073 int length = read();
074 if (length < 0)
075 {
076 throw new IOException("EOF found when length expected");
077 }
078
079 if (length == 0x80)
080 {
081 return -1; // indefinite-length encoding
082 }
083
084 if (length > 127)
085 {
086 int size = length & 0x7f;
087
088 if (size > 4)
089 {
090 throw new IOException("DER length more than 4 bytes");
091 }
092
093 length = 0;
094 for (int i = 0; i < size; i++)
095 {
096 int next = read();
097
098 if (next < 0)
099 {
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 // with tagged object tag number is bottom 5 bits
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) // empty tag!
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 // simple type - implicit... return an octet string
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 // explicitly tagged (probably!) - if it isn't we'd have to
281 // tell from the context
282 //
283 if (aIn.available() == 0)
284 {
285 return new DERTaggedObject(tagNo, dObj);
286 }
287
288 //
289 // another implicit object, we'll create a sequence...
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) // indefinite length method
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 // with tagged object tag number is bottom 5 bits
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 // simple type - implicit... return an octet string
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 // either constructed or explicitly tagged
443 //
444 DERObject dObj = readObject();
445
446 if (dObj == END_OF_STREAM) // empty tag!
447 {
448 return new DERTaggedObject(tagNo);
449 }
450
451 DERObject next = readObject();
452
453 //
454 // explicitly tagged (probably!) - if it isn't we'd have to
455 // tell from the context
456 //
457 if (next == END_OF_STREAM)
458 {
459 return new BERTaggedObject(tagNo, dObj);
460 }
461
462 //
463 // another implicit object, we'll create a sequence...
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) // end of contents marker.
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 }