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    }