001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017
018 package org.apache.geronimo.util.asn1;
019
020 import java.io.ByteArrayInputStream;
021 import java.io.ByteArrayOutputStream;
022 import java.io.EOFException;
023 import java.io.FilterInputStream;
024 import java.io.IOException;
025 import java.io.InputStream;
026 import java.util.Vector;
027
028 /**
029 * a general purpose ASN.1 decoder - note: this class differs from the
030 * others in that it returns null after it has read the last object in
031 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
032 * returned.
033 */
034 public class ASN1InputStream
035 extends FilterInputStream
036 implements DERTags
037 {
038 private DERObject END_OF_STREAM = new DERObject() {
039 void encode(
040 DEROutputStream out)
041 throws IOException
042 {
043 throw new IOException("Eeek!");
044 }
045 public int hashCode()
046 {
047 return 0;
048 }
049 public boolean equals(
050 Object o)
051 {
052 return o == this;
053 }
054 };
055 boolean eofFound = false;
056
057 public ASN1InputStream(
058 InputStream is)
059 {
060 super(is);
061 }
062
063 public ASN1InputStream(
064 byte[] input)
065 {
066 super(new ByteArrayInputStream(input));
067 }
068
069 protected int readLength()
070 throws IOException
071 {
072 int length = read();
073 if (length < 0)
074 {
075 throw new IOException("EOF found when length expected");
076 }
077
078 if (length == 0x80)
079 {
080 return -1; // indefinite-length encoding
081 }
082
083 if (length > 127)
084 {
085 int size = length & 0x7f;
086
087 if (size > 4)
088 {
089 throw new IOException("DER length more than 4 bytes");
090 }
091
092 length = 0;
093 for (int i = 0; i < size; i++)
094 {
095 int next = read();
096
097 if (next < 0)
098 {
099 throw new IOException("EOF found reading length");
100 }
101
102 length = (length << 8) + next;
103 }
104
105 if (length < 0)
106 {
107 throw new IOException("corrupted steam - negative length found");
108 }
109 }
110
111 return length;
112 }
113
114 protected void readFully(
115 byte[] bytes)
116 throws IOException
117 {
118 int left = bytes.length;
119 int len;
120
121 if (left == 0)
122 {
123 return;
124 }
125
126 while ((len = read(bytes, bytes.length - left, left)) > 0)
127 {
128 if ((left -= len) == 0)
129 {
130 return;
131 }
132 }
133
134 if (left != 0)
135 {
136 throw new EOFException("EOF encountered in middle of object");
137 }
138 }
139
140 /**
141 * build an object given its tag and a byte stream to construct it
142 * from.
143 */
144 protected DERObject buildObject(
145 int tag,
146 byte[] bytes)
147 throws IOException
148 {
149 if ((tag & APPLICATION) != 0)
150 {
151 return new DERApplicationSpecific(tag, bytes);
152 }
153
154 switch (tag)
155 {
156 case NULL:
157 return new DERNull();
158 case SEQUENCE | CONSTRUCTED:
159 ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
160 ASN1InputStream aIn = new ASN1InputStream(bIn);
161 ASN1EncodableVector v = new ASN1EncodableVector();
162
163 DERObject obj = aIn.readObject();
164
165 while (obj != null)
166 {
167 v.add(obj);
168 obj = aIn.readObject();
169 }
170
171 return new DERSequence(v);
172 case SET | CONSTRUCTED:
173 bIn = new ByteArrayInputStream(bytes);
174 aIn = new ASN1InputStream(bIn);
175 v = new ASN1EncodableVector();
176
177 obj = aIn.readObject();
178
179 while (obj != null)
180 {
181 v.add(obj);
182 obj = aIn.readObject();
183 }
184
185 return new DERSet(v, false);
186 case BOOLEAN:
187 return new DERBoolean(bytes);
188 case INTEGER:
189 return new DERInteger(bytes);
190 case ENUMERATED:
191 return new DEREnumerated(bytes);
192 case OBJECT_IDENTIFIER:
193 return new DERObjectIdentifier(bytes);
194 case BIT_STRING:
195 int padBits = bytes[0];
196 byte[] data = new byte[bytes.length - 1];
197
198 System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
199
200 return new DERBitString(data, padBits);
201 case NUMERIC_STRING:
202 return new DERNumericString(bytes);
203 case UTF8_STRING:
204 return new DERUTF8String(bytes);
205 case PRINTABLE_STRING:
206 return new DERPrintableString(bytes);
207 case IA5_STRING:
208 return new DERIA5String(bytes);
209 case T61_STRING:
210 return new DERT61String(bytes);
211 case VISIBLE_STRING:
212 return new DERVisibleString(bytes);
213 case GENERAL_STRING:
214 return new DERGeneralString(bytes);
215 case UNIVERSAL_STRING:
216 return new DERUniversalString(bytes);
217 case BMP_STRING:
218 return new DERBMPString(bytes);
219 case OCTET_STRING:
220 return new DEROctetString(bytes);
221 case UTC_TIME:
222 return new DERUTCTime(bytes);
223 case GENERALIZED_TIME:
224 return new DERGeneralizedTime(bytes);
225 default:
226 //
227 // with tagged object tag number is bottom 5 bits
228 //
229 if ((tag & TAGGED) != 0)
230 {
231 int tagNo = tag & 0x1f;
232
233 if (tagNo == 0x1f)
234 {
235 int idx = 0;
236
237 tagNo = 0;
238
239 while ((bytes[idx] & 0x80) != 0)
240 {
241 tagNo |= (bytes[idx++] & 0x7f);
242 tagNo <<= 7;
243 }
244
245 tagNo |= (bytes[idx] & 0x7f);
246
247 byte[] tmp = bytes;
248
249 bytes = new byte[tmp.length - (idx + 1)];
250 System.arraycopy(tmp, idx + 1, bytes, 0, bytes.length);
251 }
252
253 if (bytes.length == 0) // empty tag!
254 {
255 if ((tag & CONSTRUCTED) == 0)
256 {
257 return new DERTaggedObject(false, tagNo, new DERNull());
258 }
259 else
260 {
261 return new DERTaggedObject(false, tagNo, new DERSequence());
262 }
263 }
264
265 //
266 // simple type - implicit... return an octet string
267 //
268 if ((tag & CONSTRUCTED) == 0)
269 {
270 return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
271 }
272
273 bIn = new ByteArrayInputStream(bytes);
274 aIn = new ASN1InputStream(bIn);
275
276 DEREncodable dObj = aIn.readObject();
277
278 //
279 // explicitly tagged (probably!) - if it isn't we'd have to
280 // tell from the context
281 //
282 if (aIn.available() == 0)
283 {
284 return new DERTaggedObject(tagNo, dObj);
285 }
286
287 //
288 // another implicit object, we'll create a sequence...
289 //
290 v = new ASN1EncodableVector();
291
292 while (dObj != null)
293 {
294 v.add(dObj);
295 dObj = aIn.readObject();
296 }
297
298 return new DERTaggedObject(false, tagNo, new DERSequence(v));
299 }
300
301 return new DERUnknownTag(tag, bytes);
302 }
303 }
304
305 /**
306 * read a string of bytes representing an indefinite length object.
307 */
308 private byte[] readIndefiniteLengthFully()
309 throws IOException
310 {
311 ByteArrayOutputStream bOut = new ByteArrayOutputStream();
312 int b, b1;
313
314 b1 = read();
315
316 while ((b = read()) >= 0)
317 {
318 if (b1 == 0 && b == 0)
319 {
320 break;
321 }
322
323 bOut.write(b1);
324 b1 = b;
325 }
326
327 return bOut.toByteArray();
328 }
329
330 private BERConstructedOctetString buildConstructedOctetString()
331 throws IOException
332 {
333 Vector octs = new Vector();
334
335 for (;;)
336 {
337 DERObject o = readObject();
338
339 if (o == END_OF_STREAM)
340 {
341 break;
342 }
343
344 octs.addElement(o);
345 }
346
347 return new BERConstructedOctetString(octs);
348 }
349
350 public DERObject readObject()
351 throws IOException
352 {
353 int tag = read();
354 if (tag == -1)
355 {
356 if (eofFound)
357 {
358 throw new EOFException("attempt to read past end of file.");
359 }
360
361 eofFound = true;
362
363 return null;
364 }
365
366 int length = readLength();
367
368 if (length < 0) // indefinite length method
369 {
370 switch (tag)
371 {
372 case NULL:
373 return new BERNull();
374 case SEQUENCE | CONSTRUCTED:
375 ASN1EncodableVector v = new ASN1EncodableVector();
376
377 for (;;)
378 {
379 DERObject obj = readObject();
380
381 if (obj == END_OF_STREAM)
382 {
383 break;
384 }
385
386 v.add(obj);
387 }
388 return new BERSequence(v);
389 case SET | CONSTRUCTED:
390 v = new ASN1EncodableVector();
391
392 for (;;)
393 {
394 DERObject obj = readObject();
395
396 if (obj == END_OF_STREAM)
397 {
398 break;
399 }
400
401 v.add(obj);
402 }
403 return new BERSet(v, false);
404 case OCTET_STRING | CONSTRUCTED:
405 return buildConstructedOctetString();
406 default:
407 //
408 // with tagged object tag number is bottom 5 bits
409 //
410 if ((tag & TAGGED) != 0)
411 {
412 int tagNo = tag & 0x1f;
413
414 if (tagNo == 0x1f)
415 {
416 int b = read();
417
418 tagNo = 0;
419
420 while ((b >= 0) && ((b & 0x80) != 0))
421 {
422 tagNo |= (b & 0x7f);
423 tagNo <<= 7;
424 b = read();
425 }
426
427 tagNo |= (b & 0x7f);
428 }
429
430 //
431 // simple type - implicit... return an octet string
432 //
433 if ((tag & CONSTRUCTED) == 0)
434 {
435 byte[] bytes = readIndefiniteLengthFully();
436
437 return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
438 }
439
440 //
441 // either constructed or explicitly tagged
442 //
443 DERObject dObj = readObject();
444
445 if (dObj == END_OF_STREAM) // empty tag!
446 {
447 return new DERTaggedObject(tagNo);
448 }
449
450 DERObject next = readObject();
451
452 //
453 // explicitly tagged (probably!) - if it isn't we'd have to
454 // tell from the context
455 //
456 if (next == END_OF_STREAM)
457 {
458 return new BERTaggedObject(tagNo, dObj);
459 }
460
461 //
462 // another implicit object, we'll create a sequence...
463 //
464 v = new ASN1EncodableVector();
465
466 v.add(dObj);
467
468 do
469 {
470 v.add(next);
471 next = readObject();
472 }
473 while (next != END_OF_STREAM);
474
475 return new BERTaggedObject(false, tagNo, new BERSequence(v));
476 }
477
478 throw new IOException("unknown BER object encountered");
479 }
480 }
481 else
482 {
483 if (tag == 0 && length == 0) // end of contents marker.
484 {
485 return END_OF_STREAM;
486 }
487
488 byte[] bytes = new byte[length];
489
490 readFully(bytes);
491
492 return buildObject(tag, bytes);
493 }
494 }
495 }