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