package org.apache.commons.ssl.asn1; import java.io.ByteArrayInputStream; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; public class ASN1StreamParser { InputStream _in; private int _limit; private boolean _eofFound; public ASN1StreamParser( InputStream in) { this(in, Integer.MAX_VALUE); } public ASN1StreamParser( InputStream in, int limit) { this._in = in; this._limit = limit; } public ASN1StreamParser( byte[] encoding) { this(new ByteArrayInputStream(encoding), encoding.length); } InputStream getParentStream() { return _in; } private int readLength() throws IOException { int length = _in.read(); if (length < 0) { throw new EOFException("EOF found when length expected"); } if (length == 0x80) { return -1; // indefinite-length encoding } if (length > 127) { int size = length & 0x7f; if (size > 4) { throw new IOException("DER length more than 4 bytes"); } length = 0; for (int i = 0; i < size; i++) { int next = _in.read(); if (next < 0) { throw new EOFException("EOF found reading length"); } length = (length << 8) + next; } if (length < 0) { throw new IOException("corrupted stream - negative length found"); } if (length >= _limit) // after all we must have read at least 1 byte { throw new IOException("corrupted stream - out of bounds length found"); } } return length; } public DEREncodable readObject() throws IOException { int tag = _in.read(); if (tag == -1) { if (_eofFound) { throw new EOFException("attempt to read past end of file."); } _eofFound = true; return null; } // // turn of looking for "00" while we resolve the tag // set00Check(false); // // calculate tag number // int baseTagNo = tag & ~DERTags.CONSTRUCTED; int tagNo = baseTagNo; if ((tag & DERTags.TAGGED) != 0) { tagNo = tag & 0x1f; // // with tagged object tag number is bottom 5 bits, or stored at the start of the content // if (tagNo == 0x1f) { tagNo = 0; int b = _in.read(); while ((b >= 0) && ((b & 0x80) != 0)) { tagNo |= (b & 0x7f); tagNo <<= 7; b = _in.read(); } if (b < 0) { _eofFound = true; throw new EOFException("EOF encountered inside tag value."); } tagNo |= (b & 0x7f); } } // // calculate length // int length = readLength(); if (length < 0) // indefinite length { IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in); switch (baseTagNo) { case DERTags.NULL: while (indIn.read() >= 0) { // make sure we skip to end of object } return BERNull.INSTANCE; case DERTags.OCTET_STRING: return new BEROctetStringParser(new ASN1ObjectParser(tag, tagNo, indIn)); case DERTags.SEQUENCE: return new BERSequenceParser(new ASN1ObjectParser(tag, tagNo, indIn)); case DERTags.SET: return new BERSetParser(new ASN1ObjectParser(tag, tagNo, indIn)); default: return new BERTaggedObjectParser(tag, tagNo, indIn); } } else { DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); switch (baseTagNo) { case DERTags.INTEGER: return new DERInteger(defIn.toByteArray()); case DERTags.NULL: defIn.toByteArray(); // make sure we read to end of object bytes. return DERNull.INSTANCE; case DERTags.OBJECT_IDENTIFIER: return new DERObjectIdentifier(defIn.toByteArray()); case DERTags.OCTET_STRING: return new DEROctetString(defIn.toByteArray()); case DERTags.SEQUENCE: return new DERSequence(loadVector(defIn, length)).parser(); case DERTags.SET: return new DERSet(loadVector(defIn, length)).parser(); default: return new BERTaggedObjectParser(tag, tagNo, defIn); } } } private void set00Check(boolean enabled) { if (_in instanceof IndefiniteLengthInputStream) { ((IndefiniteLengthInputStream) _in).setEofOn00(enabled); } } private ASN1EncodableVector loadVector(InputStream in, int length) throws IOException { ASN1InputStream aIn = new ASN1InputStream(in, length); ASN1EncodableVector v = new ASN1EncodableVector(); DERObject obj; while ((obj = aIn.readObject()) != null) { v.add(obj); } return v; } }