001 package org.apache.commons.ssl.asn1;
002
003 import java.io.ByteArrayInputStream;
004 import java.io.EOFException;
005 import java.io.IOException;
006 import java.io.InputStream;
007
008 public class ASN1StreamParser {
009 InputStream _in;
010
011 private int _limit;
012 private boolean _eofFound;
013
014 public ASN1StreamParser(
015 InputStream in) {
016 this(in, Integer.MAX_VALUE);
017 }
018
019 public ASN1StreamParser(
020 InputStream in,
021 int limit) {
022 this._in = in;
023 this._limit = limit;
024 }
025
026 public ASN1StreamParser(
027 byte[] encoding) {
028 this(new ByteArrayInputStream(encoding), encoding.length);
029 }
030
031 InputStream getParentStream() {
032 return _in;
033 }
034
035 private int readLength()
036 throws IOException {
037 int length = _in.read();
038 if (length < 0) {
039 throw new EOFException("EOF found when length expected");
040 }
041
042 if (length == 0x80) {
043 return -1; // indefinite-length encoding
044 }
045
046 if (length > 127) {
047 int size = length & 0x7f;
048
049 if (size > 4) {
050 throw new IOException("DER length more than 4 bytes");
051 }
052
053 length = 0;
054 for (int i = 0; i < size; i++) {
055 int next = _in.read();
056
057 if (next < 0) {
058 throw new EOFException("EOF found reading length");
059 }
060
061 length = (length << 8) + next;
062 }
063
064 if (length < 0) {
065 throw new IOException("corrupted stream - negative length found");
066 }
067
068 if (length >= _limit) // after all we must have read at least 1 byte
069 {
070 throw new IOException("corrupted stream - out of bounds length found");
071 }
072 }
073
074 return length;
075 }
076
077 public DEREncodable readObject()
078 throws IOException {
079 int tag = _in.read();
080 if (tag == -1) {
081 if (_eofFound) {
082 throw new EOFException("attempt to read past end of file.");
083 }
084
085 _eofFound = true;
086
087 return null;
088 }
089
090 //
091 // turn of looking for "00" while we resolve the tag
092 //
093 set00Check(false);
094
095 //
096 // calculate tag number
097 //
098 int baseTagNo = tag & ~DERTags.CONSTRUCTED;
099 int tagNo = baseTagNo;
100
101 if ((tag & DERTags.TAGGED) != 0) {
102 tagNo = tag & 0x1f;
103
104 //
105 // with tagged object tag number is bottom 5 bits, or stored at the start of the content
106 //
107 if (tagNo == 0x1f) {
108 tagNo = 0;
109
110 int b = _in.read();
111
112 while ((b >= 0) && ((b & 0x80) != 0)) {
113 tagNo |= (b & 0x7f);
114 tagNo <<= 7;
115 b = _in.read();
116 }
117
118 if (b < 0) {
119 _eofFound = true;
120
121 throw new EOFException("EOF encountered inside tag value.");
122 }
123
124 tagNo |= (b & 0x7f);
125 }
126 }
127
128 //
129 // calculate length
130 //
131 int length = readLength();
132
133 if (length < 0) // indefinite length
134 {
135 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in);
136
137 switch (baseTagNo) {
138 case DERTags.NULL:
139 while (indIn.read() >= 0) {
140 // make sure we skip to end of object
141 }
142 return BERNull.INSTANCE;
143 case DERTags.OCTET_STRING:
144 return new BEROctetStringParser(new ASN1ObjectParser(tag, tagNo, indIn));
145 case DERTags.SEQUENCE:
146 return new BERSequenceParser(new ASN1ObjectParser(tag, tagNo, indIn));
147 case DERTags.SET:
148 return new BERSetParser(new ASN1ObjectParser(tag, tagNo, indIn));
149 default:
150 return new BERTaggedObjectParser(tag, tagNo, indIn);
151 }
152 } else {
153 DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
154
155 switch (baseTagNo) {
156 case DERTags.INTEGER:
157 return new DERInteger(defIn.toByteArray());
158 case DERTags.NULL:
159 defIn.toByteArray(); // make sure we read to end of object bytes.
160 return DERNull.INSTANCE;
161 case DERTags.OBJECT_IDENTIFIER:
162 return new DERObjectIdentifier(defIn.toByteArray());
163 case DERTags.OCTET_STRING:
164 return new DEROctetString(defIn.toByteArray());
165 case DERTags.SEQUENCE:
166 return new DERSequence(loadVector(defIn, length)).parser();
167 case DERTags.SET:
168 return new DERSet(loadVector(defIn, length)).parser();
169 default:
170 return new BERTaggedObjectParser(tag, tagNo, defIn);
171 }
172 }
173 }
174
175 private void set00Check(boolean enabled) {
176 if (_in instanceof IndefiniteLengthInputStream) {
177 ((IndefiniteLengthInputStream) _in).setEofOn00(enabled);
178 }
179 }
180
181 private ASN1EncodableVector loadVector(InputStream in, int length)
182 throws IOException {
183 ASN1InputStream aIn = new ASN1InputStream(in, length);
184 ASN1EncodableVector v = new ASN1EncodableVector();
185
186 DERObject obj;
187 while ((obj = aIn.readObject()) != null) {
188 v.add(obj);
189 }
190
191 return v;
192 }
193 }