1 package org.bouncycastle.asn1; 2 3 import java.io.ByteArrayInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 7 public class ASN1StreamParser 8 { 9 private final InputStream _in; 10 private final int _limit; 11 12 public ASN1StreamParser( 13 InputStream in) 14 { 15 this(in, ASN1InputStream.findLimit(in)); 16 } 17 18 public ASN1StreamParser( 19 InputStream in, 20 int limit) 21 { 22 this._in = in; 23 this._limit = limit; 24 } 25 26 public ASN1StreamParser( 27 byte[] encoding) 28 { 29 this(new ByteArrayInputStream(encoding), encoding.length); 30 } 31 32 DEREncodable readIndef(int tagValue) throws IOException 33 { 34 // Note: INDEF => CONSTRUCTED 35 36 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 37 switch (tagValue) 38 { 39 case DERTags.EXTERNAL: 40 return new DERExternalParser(this); 41 case DERTags.OCTET_STRING: 42 return new BEROctetStringParser(this); 43 case DERTags.SEQUENCE: 44 return new BERSequenceParser(this); 45 case DERTags.SET: 46 return new BERSetParser(this); 47 default: 48 throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue)); 49 } 50 } 51 52 DEREncodable readImplicit(boolean constructed, int tag) throws IOException 53 { 54 if (_in instanceof IndefiniteLengthInputStream) 55 { 56 if (!constructed) 57 { 58 throw new IOException("indefinite length primitive encoding encountered"); 59 } 60 61 return readIndef(tag); 62 } 63 64 if (constructed) 65 { 66 switch (tag) 67 { 68 case DERTags.SET: 69 return new DERSetParser(this); 70 case DERTags.SEQUENCE: 71 return new DERSequenceParser(this); 72 case DERTags.OCTET_STRING: 73 return new BEROctetStringParser(this); 74 } 75 } 76 else 77 { 78 switch (tag) 79 { 80 case DERTags.SET: 81 throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); 82 case DERTags.SEQUENCE: 83 throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); 84 case DERTags.OCTET_STRING: 85 return new DEROctetStringParser((DefiniteLengthInputStream)_in); 86 } 87 } 88 89 // TODO ASN1Exception 90 throw new RuntimeException("implicit tagging not implemented"); 91 } 92 93 DERObject readTaggedObject(boolean constructed, int tag) throws IOException 94 { 95 if (!constructed) 96 { 97 // Note: !CONSTRUCTED => IMPLICIT 98 DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_in; 99 return new DERTaggedObject(false, tag, new DEROctetString(defIn.toByteArray())); 100 } 101 102 ASN1EncodableVector v = readVector(); 103 104 if (_in instanceof IndefiniteLengthInputStream) 105 { 106 return v.size() == 1 107 ? new BERTaggedObject(true, tag, v.get(0)) 108 : new BERTaggedObject(false, tag, BERFactory.createSequence(v)); 109 } 110 111 return v.size() == 1 112 ? new DERTaggedObject(true, tag, v.get(0)) 113 : new DERTaggedObject(false, tag, DERFactory.createSequence(v)); 114 } 115 116 public DEREncodable readObject() 117 throws IOException 118 { 119 int tag = _in.read(); 120 if (tag == -1) 121 { 122 return null; 123 } 124 125 // 126 // turn of looking for "00" while we resolve the tag 127 // 128 set00Check(false); 129 130 // 131 // calculate tag number 132 // 133 int tagNo = ASN1InputStream.readTagNumber(_in, tag); 134 135 boolean isConstructed = (tag & DERTags.CONSTRUCTED) != 0; 136 137 // 138 // calculate length 139 // 140 int length = ASN1InputStream.readLength(_in, _limit); 141 142 if (length < 0) // indefinite length method 143 { 144 if (!isConstructed) 145 { 146 throw new IOException("indefinite length primitive encoding encountered"); 147 } 148 149 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); 150 ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); 151 152 if ((tag & DERTags.APPLICATION) != 0) 153 { 154 return new BERApplicationSpecificParser(tagNo, sp); 155 } 156 157 if ((tag & DERTags.TAGGED) != 0) 158 { 159 return new BERTaggedObjectParser(true, tagNo, sp); 160 } 161 162 return sp.readIndef(tagNo); 163 } 164 else 165 { 166 DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); 167 168 if ((tag & DERTags.APPLICATION) != 0) 169 { 170 return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); 171 } 172 173 if ((tag & DERTags.TAGGED) != 0) 174 { 175 return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn)); 176 } 177 178 if (isConstructed) 179 { 180 // TODO There are other tags that may be constructed (e.g. BIT_STRING) 181 switch (tagNo) 182 { 183 case DERTags.OCTET_STRING: 184 // 185 // yes, people actually do this... 186 // 187 return new BEROctetStringParser(new ASN1StreamParser(defIn)); 188 case DERTags.SEQUENCE: 189 return new DERSequenceParser(new ASN1StreamParser(defIn)); 190 case DERTags.SET: 191 return new DERSetParser(new ASN1StreamParser(defIn)); 192 case DERTags.EXTERNAL: 193 return new DERExternalParser(new ASN1StreamParser(defIn)); 194 default: 195 // TODO Add DERUnknownTagParser class? 196 return new DERUnknownTag(true, tagNo, defIn.toByteArray()); 197 } 198 } 199 200 // Some primitive encodings can be handled by parsers too... 201 switch (tagNo) 202 { 203 case DERTags.OCTET_STRING: 204 return new DEROctetStringParser(defIn); 205 } 206 207 try 208 { 209 return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn.toByteArray()); 210 } 211 catch (IllegalArgumentException e) 212 { 213 throw new ASN1Exception("corrupted stream detected", e); 214 } 215 } 216 } 217 218 private void set00Check(boolean enabled) 219 { 220 if (_in instanceof IndefiniteLengthInputStream) 221 { 222 ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); 223 } 224 } 225 226 ASN1EncodableVector readVector() throws IOException 227 { 228 ASN1EncodableVector v = new ASN1EncodableVector(); 229 230 DEREncodable obj; 231 while ((obj = readObject()) != null) 232 { 233 if (obj instanceof InMemoryRepresentable) 234 { 235 v.add(((InMemoryRepresentable)obj).getLoadedObject()); 236 } 237 else 238 { 239 v.add(obj.getDERObject()); 240 } 241 } 242 243 return v; 244 } 245 } 246