View Javadoc

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;      // indefinite-length encoding
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             // 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 }