001 package org.apache.commons.ssl.asn1;
002
003 import java.io.ByteArrayInputStream;
004 import java.io.ByteArrayOutputStream;
005 import java.io.EOFException;
006 import java.io.FilterInputStream;
007 import java.io.IOException;
008 import java.io.InputStream;
009 import java.util.Vector;
010
011 /**
012 * a general purpose ASN.1 decoder - note: this class differs from the
013 * others in that it returns null after it has read the last object in
014 * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is
015 * returned.
016 */
017 public class ASN1InputStream
018 extends FilterInputStream
019 implements DERTags {
020 private static final DERObject END_OF_STREAM = new DERObject() {
021 void encode(
022 DEROutputStream out)
023 throws IOException {
024 throw new IOException("Eeek!");
025 }
026 public int hashCode() {
027 return 0;
028 }
029 public boolean equals(
030 Object o) {
031 return o == this;
032 }
033 };
034
035 boolean eofFound = false;
036 int limit = Integer.MAX_VALUE;
037
038 public ASN1InputStream(
039 InputStream is) {
040 super(is);
041 }
042
043 /**
044 * Create an ASN1InputStream based on the input byte array. The length of DER objects in
045 * the stream is automatically limited to the length of the input array.
046 *
047 * @param input array containing ASN.1 encoded data.
048 */
049 public ASN1InputStream(
050 byte[] input) {
051 this(new ByteArrayInputStream(input), input.length);
052 }
053
054 /**
055 * Create an ASN1InputStream where no DER object will be longer than limit.
056 *
057 * @param input stream containing ASN.1 encoded data.
058 * @param limit maximum size of a DER encoded object.
059 */
060 public ASN1InputStream(
061 InputStream input,
062 int limit) {
063 super(input);
064 this.limit = limit;
065 }
066
067 protected int readLength()
068 throws IOException {
069 int length = read();
070 if (length < 0) {
071 throw new IOException("EOF found when length expected");
072 }
073
074 if (length == 0x80) {
075 return -1; // indefinite-length encoding
076 }
077
078 if (length > 127) {
079 int size = length & 0x7f;
080
081 if (size > 4) {
082 throw new IOException("DER length more than 4 bytes");
083 }
084
085 length = 0;
086 for (int i = 0; i < size; i++) {
087 int next = read();
088
089 if (next < 0) {
090 throw new IOException("EOF found reading length");
091 }
092
093 length = (length << 8) + next;
094 }
095
096 if (length < 0) {
097 throw new IOException("corrupted stream - negative length found");
098 }
099
100 if (length >= limit) // after all we must have read at least 1 byte
101 {
102 throw new IOException("corrupted stream - out of bounds length found");
103 }
104 }
105
106 return length;
107 }
108
109 protected void readFully(
110 byte[] bytes)
111 throws IOException {
112 int left = bytes.length;
113 int len;
114
115 if (left == 0) {
116 return;
117 }
118
119 while ((len = read(bytes, bytes.length - left, left)) > 0) {
120 if ((left -= len) == 0) {
121 return;
122 }
123 }
124
125 if (left != 0) {
126 throw new EOFException("EOF encountered in middle of object");
127 }
128 }
129
130 /** build an object given its tag and the number of bytes to construct it from. */
131 protected DERObject buildObject(
132 int tag,
133 int tagNo,
134 int length)
135 throws IOException {
136 if ((tag & APPLICATION) != 0) {
137 return new DERApplicationSpecific(tagNo, readDefiniteLengthFully(length));
138 }
139
140 boolean isConstructed = (tag & CONSTRUCTED) != 0;
141
142 if (isConstructed) {
143 switch (tag) {
144 case SEQUENCE | CONSTRUCTED:
145 return new DERSequence(buildDerEncodableVector(length));
146 case SET | CONSTRUCTED:
147 return new DERSet(buildDerEncodableVector(length), false);
148 case OCTET_STRING | CONSTRUCTED:
149 return buildDerConstructedOctetString(length);
150 default: {
151 //
152 // with tagged object tag number is bottom 5 bits
153 //
154 if ((tag & TAGGED) != 0) {
155 if (length == 0) // empty tag!
156 {
157 return new DERTaggedObject(false, tagNo, new DERSequence());
158 }
159
160 ASN1EncodableVector v = buildDerEncodableVector(length);
161
162 if (v.size() == 1) {
163 //
164 // explicitly tagged (probably!) - if it isn't we'd have to
165 // tell from the context
166 //
167 return new DERTaggedObject(tagNo, v.get(0));
168 }
169
170 return new DERTaggedObject(false, tagNo, new DERSequence(v));
171 }
172
173 return new DERUnknownTag(tag, readDefiniteLengthFully(length));
174 }
175 }
176 }
177
178 byte[] bytes = readDefiniteLengthFully(length);
179
180 switch (tag) {
181 case NULL:
182 return DERNull.INSTANCE;
183 case BOOLEAN:
184 return new DERBoolean(bytes);
185 case INTEGER:
186 return new DERInteger(bytes);
187 case ENUMERATED:
188 return new DEREnumerated(bytes);
189 case OBJECT_IDENTIFIER:
190 return new DERObjectIdentifier(bytes);
191 case BIT_STRING: {
192 int padBits = bytes[0];
193 byte[] data = new byte[bytes.length - 1];
194
195 System.arraycopy(bytes, 1, data, 0, bytes.length - 1);
196
197 return new DERBitString(data, padBits);
198 }
199 case NUMERIC_STRING:
200 return new DERNumericString(bytes);
201 case UTF8_STRING:
202 return new DERUTF8String(bytes);
203 case PRINTABLE_STRING:
204 return new DERPrintableString(bytes);
205 case IA5_STRING:
206 return new DERIA5String(bytes);
207 case T61_STRING:
208 return new DERT61String(bytes);
209 case VISIBLE_STRING:
210 return new DERVisibleString(bytes);
211 case GENERAL_STRING:
212 return new DERGeneralString(bytes);
213 case UNIVERSAL_STRING:
214 return new DERUniversalString(bytes);
215 case BMP_STRING:
216 return new DERBMPString(bytes);
217 case OCTET_STRING:
218 return new DEROctetString(bytes);
219 case UTC_TIME:
220 return new DERUTCTime(bytes);
221 case GENERALIZED_TIME:
222 return new DERGeneralizedTime(bytes);
223 default: {
224 //
225 // with tagged object tag number is bottom 5 bits
226 //
227 if ((tag & TAGGED) != 0) {
228 if (bytes.length == 0) // empty tag!
229 {
230 return new DERTaggedObject(false, tagNo, DERNull.INSTANCE);
231 }
232
233 //
234 // simple type - implicit... return an octet string
235 //
236 return new DERTaggedObject(false, tagNo, new DEROctetString(bytes));
237 }
238
239 return new DERUnknownTag(tag, bytes);
240 }
241 }
242 }
243
244 private byte[] readDefiniteLengthFully(int length)
245 throws IOException {
246 byte[] bytes = new byte[length];
247 readFully(bytes);
248 return bytes;
249 }
250
251 /** read a string of bytes representing an indefinite length object. */
252 private byte[] readIndefiniteLengthFully()
253 throws IOException {
254 ByteArrayOutputStream bOut = new ByteArrayOutputStream();
255 int b, b1;
256
257 b1 = read();
258
259 while ((b = read()) >= 0) {
260 if (b1 == 0 && b == 0) {
261 break;
262 }
263
264 bOut.write(b1);
265 b1 = b;
266 }
267
268 return bOut.toByteArray();
269 }
270
271 private BERConstructedOctetString buildConstructedOctetString(DERObject sentinel)
272 throws IOException {
273 Vector octs = new Vector();
274 DERObject o;
275
276 while ((o = readObject()) != sentinel) {
277 octs.addElement(o);
278 }
279
280 return new BERConstructedOctetString(octs);
281 }
282
283 //
284 // yes, people actually do this...
285 //
286 private BERConstructedOctetString buildDerConstructedOctetString(int length)
287 throws IOException {
288 DefiniteLengthInputStream dIn = new DefiniteLengthInputStream(this, length);
289 ASN1InputStream aIn = new ASN1InputStream(dIn, length);
290
291 return aIn.buildConstructedOctetString(null);
292 }
293
294 private ASN1EncodableVector buildEncodableVector(DERObject sentinel)
295 throws IOException {
296 ASN1EncodableVector v = new ASN1EncodableVector();
297 DERObject o;
298
299 while ((o = readObject()) != sentinel) {
300 v.add(o);
301 }
302
303 return v;
304 }
305
306 private ASN1EncodableVector buildDerEncodableVector(int length)
307 throws IOException {
308 DefiniteLengthInputStream dIn = new DefiniteLengthInputStream(this, length);
309 ASN1InputStream aIn = new ASN1InputStream(dIn, length);
310
311 return aIn.buildEncodableVector(null);
312 }
313
314 public DERObject readObject()
315 throws IOException {
316 int tag = read();
317 if (tag == -1) {
318 if (eofFound) {
319 throw new EOFException("attempt to read past end of file.");
320 }
321
322 eofFound = true;
323
324 return null;
325 }
326
327 int tagNo = 0;
328
329 if ((tag & TAGGED) != 0 || (tag & APPLICATION) != 0) {
330 tagNo = readTagNumber(tag);
331 }
332
333 int length = readLength();
334
335 if (length < 0) // indefinite length method
336 {
337 switch (tag) {
338 case NULL:
339 return BERNull.INSTANCE;
340 case SEQUENCE | CONSTRUCTED:
341 return new BERSequence(buildEncodableVector(END_OF_STREAM));
342 case SET | CONSTRUCTED:
343 return new BERSet(buildEncodableVector(END_OF_STREAM), false);
344 case OCTET_STRING | CONSTRUCTED:
345 return buildConstructedOctetString(END_OF_STREAM);
346 default: {
347 //
348 // with tagged object tag number is bottom 5 bits
349 //
350 if ((tag & TAGGED) != 0) {
351 //
352 // simple type - implicit... return an octet string
353 //
354 if ((tag & CONSTRUCTED) == 0) {
355 byte[] bytes = readIndefiniteLengthFully();
356
357 return new BERTaggedObject(false, tagNo, new DEROctetString(bytes));
358 }
359
360 //
361 // either constructed or explicitly tagged
362 //
363 ASN1EncodableVector v = buildEncodableVector(END_OF_STREAM);
364
365 if (v.size() == 0) // empty tag!
366 {
367 return new DERTaggedObject(tagNo);
368 }
369
370 if (v.size() == 1) {
371 //
372 // explicitly tagged (probably!) - if it isn't we'd have to
373 // tell from the context
374 //
375 return new BERTaggedObject(tagNo, v.get(0));
376 }
377
378 return new BERTaggedObject(false, tagNo, new BERSequence(v));
379 }
380
381 throw new IOException("unknown BER object encountered");
382 }
383 }
384 } else {
385 if (tag == 0 && length == 0) // end of contents marker.
386 {
387 return END_OF_STREAM;
388 }
389
390 return buildObject(tag, tagNo, length);
391 }
392 }
393
394 private int readTagNumber(int tag)
395 throws IOException {
396 int tagNo = tag & 0x1f;
397
398 if (tagNo == 0x1f) {
399 int b = read();
400
401 tagNo = 0;
402
403 while ((b >= 0) && ((b & 0x80) != 0)) {
404 tagNo |= (b & 0x7f);
405 tagNo <<= 7;
406 b = read();
407 }
408
409 if (b < 0) {
410 eofFound = true;
411 throw new EOFException("EOF found inside tag value.");
412 }
413
414 tagNo |= (b & 0x7f);
415 }
416
417 return tagNo;
418 }
419 }
420