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