001package org.apache.commons.ssl.asn1;
002
003import java.io.ByteArrayInputStream;
004import java.io.EOFException;
005import java.io.FilterInputStream;
006import java.io.IOException;
007import java.io.InputStream;
008
009/**
010 * Don't use this class. It will eventually disappear, use ASN1InputStream.
011 * <br>
012 * This class is scheduled for removal.
013 *
014 * @deprecated use ASN1InputStream
015 */
016public class DERInputStream
017    extends FilterInputStream implements DERTags {
018    /** @deprecated use ASN1InputStream */
019    public DERInputStream(
020        InputStream is) {
021        super(is);
022    }
023
024    protected int readLength()
025        throws IOException {
026        int length = read();
027        if (length < 0) {
028            throw new IOException("EOF found when length expected");
029        }
030
031        if (length == 0x80) {
032            return -1;      // indefinite-length encoding
033        }
034
035        if (length > 127) {
036            int size = length & 0x7f;
037
038            if (size > 4) {
039                throw new IOException("DER length more than 4 bytes");
040            }
041
042            length = 0;
043            for (int i = 0; i < size; i++) {
044                int next = read();
045
046                if (next < 0) {
047                    throw new IOException("EOF found reading length");
048                }
049
050                length = (length << 8) + next;
051            }
052
053            if (length < 0) {
054                throw new IOException("corrupted stream - negative length found");
055            }
056        }
057
058        return length;
059    }
060
061    protected void readFully(
062        byte[] bytes)
063        throws IOException {
064        int left = bytes.length;
065
066        if (left == 0) {
067            return;
068        }
069
070        while (left > 0) {
071            int l = read(bytes, bytes.length - left, left);
072
073            if (l < 0) {
074                throw new EOFException("unexpected end of stream");
075            }
076
077            left -= l;
078        }
079    }
080
081    /**
082     * build an object given its tag and a byte stream to construct it
083     * from.
084     */
085    protected DERObject buildObject(
086        int tag,
087        byte[] bytes)
088        throws IOException {
089        switch (tag) {
090            case NULL:
091                return null;
092            case SEQUENCE | CONSTRUCTED:
093                ByteArrayInputStream bIn = new ByteArrayInputStream(bytes);
094                BERInputStream dIn = new BERInputStream(bIn);
095                DERConstructedSequence seq = new DERConstructedSequence();
096
097                try {
098                    for (; ;) {
099                        DERObject obj = dIn.readObject();
100
101                        seq.addObject(obj);
102                    }
103                }
104                catch (EOFException ex) {
105                    return seq;
106                }
107            case SET | CONSTRUCTED:
108                bIn = new ByteArrayInputStream(bytes);
109                dIn = new BERInputStream(bIn);
110
111                ASN1EncodableVector v = new ASN1EncodableVector();
112
113                try {
114                    for (; ;) {
115                        DERObject obj = dIn.readObject();
116
117                        v.add(obj);
118                    }
119                }
120                catch (EOFException ex) {
121                    return new DERConstructedSet(v);
122                }
123            case BOOLEAN:
124                return new DERBoolean(bytes);
125            case INTEGER:
126                return new DERInteger(bytes);
127            case ENUMERATED:
128                return new DEREnumerated(bytes);
129            case OBJECT_IDENTIFIER:
130                return new DERObjectIdentifier(bytes);
131            case BIT_STRING:
132                int padBits = bytes[0];
133                byte[] data = new byte[bytes.length - 1];
134
135                System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
136
137                return new DERBitString(data, padBits);
138            case UTF8_STRING:
139                return new DERUTF8String(bytes);
140            case PRINTABLE_STRING:
141                return new DERPrintableString(bytes);
142            case IA5_STRING:
143                return new DERIA5String(bytes);
144            case T61_STRING:
145                return new DERT61String(bytes);
146            case VISIBLE_STRING:
147                return new DERVisibleString(bytes);
148            case UNIVERSAL_STRING:
149                return new DERUniversalString(bytes);
150            case GENERAL_STRING:
151                return new DERGeneralString(bytes);
152            case BMP_STRING:
153                return new DERBMPString(bytes);
154            case OCTET_STRING:
155                return new DEROctetString(bytes);
156            case UTC_TIME:
157                return new DERUTCTime(bytes);
158            case GENERALIZED_TIME:
159                return new DERGeneralizedTime(bytes);
160            default:
161                //
162                // with tagged object tag number is bottom 5 bits
163                //
164                if ((tag & TAGGED) != 0) {
165                    if ((tag & 0x1f) == 0x1f) {
166                        throw new IOException("unsupported high tag encountered");
167                    }
168
169                    if (bytes.length == 0)        // empty tag!
170                    {
171                        if ((tag & CONSTRUCTED) == 0) {
172                            return new DERTaggedObject(false, tag & 0x1f, new DERNull());
173                        } else {
174                            return new DERTaggedObject(false, tag & 0x1f, new DERConstructedSequence());
175                        }
176                    }
177
178                    //
179                    // simple type - implicit... return an octet string
180                    //
181                    if ((tag & CONSTRUCTED) == 0) {
182                        return new DERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes));
183                    }
184
185                    bIn = new ByteArrayInputStream(bytes);
186                    dIn = new BERInputStream(bIn);
187
188                    DEREncodable dObj = dIn.readObject();
189
190                    //
191                    // explicitly tagged (probably!) - if it isn't we'd have to
192                    // tell from the context
193                    //
194                    if (dIn.available() == 0) {
195                        return new DERTaggedObject(tag & 0x1f, dObj);
196                    }
197
198                    //
199                    // another implicit object, we'll create a sequence...
200                    //
201                    seq = new DERConstructedSequence();
202
203                    seq.addObject(dObj);
204
205                    try {
206                        for (; ;) {
207                            dObj = dIn.readObject();
208
209                            seq.addObject(dObj);
210                        }
211                    }
212                    catch (EOFException ex) {
213                        // ignore --
214                    }
215
216                    return new DERTaggedObject(false, tag & 0x1f, seq);
217                }
218
219                return new DERUnknownTag(tag, bytes);
220        }
221    }
222
223    public DERObject readObject()
224        throws IOException {
225        int tag = read();
226        if (tag == -1) {
227            throw new EOFException();
228        }
229
230        int length = readLength();
231        byte[] bytes = new byte[length];
232
233        readFully(bytes);
234
235        return buildObject(tag, bytes);
236    }
237}