package com.google.bitcoin.bouncycastle.asn1; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; public class ASN1StreamParser { private final InputStream _in; private final int _limit; private static int findLimit(InputStream in) { if (in instanceof DefiniteLengthInputStream) { return ((DefiniteLengthInputStream)in).getRemaining(); } return Integer.MAX_VALUE; } public ASN1StreamParser( InputStream in) { this(in, findLimit(in)); } public ASN1StreamParser( InputStream in, int limit) { this._in = in; this._limit = limit; } public ASN1StreamParser( byte[] encoding) { this(new ByteArrayInputStream(encoding), encoding.length); } public DEREncodable readObject() throws IOException { int tag = _in.read(); if (tag == -1) { return null; } // // turn of looking for "00" while we resolve the tag // set00Check(false); // // calculate tag number // int tagNo = ASN1InputStream.readTagNumber(_in, tag); boolean isConstructed = (tag & DERTags.CONSTRUCTED) != 0; // // calculate length // int length = ASN1InputStream.readLength(_in, _limit); if (length < 0) // indefinite length method { if (!isConstructed) { throw new IOException("indefinite length primitive encoding encountered"); } IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in); if ((tag & DERTags.APPLICATION) != 0) { ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); return new BERApplicationSpecificParser(tagNo, sp); } if ((tag & DERTags.TAGGED) != 0) { return new BERTaggedObjectParser(tag, tagNo, indIn); } ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagNo) { case DERTags.OCTET_STRING: return new BEROctetStringParser(sp); case DERTags.SEQUENCE: return new BERSequenceParser(sp); case DERTags.SET: return new BERSetParser(sp); case DERTags.EXTERNAL:{ return new DERExternalParser(sp); } default: throw new IOException("unknown BER object encountered: 0x" + Integer.toHexString(tagNo)); } } else { DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); if ((tag & DERTags.APPLICATION) != 0) { return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); } if ((tag & DERTags.TAGGED) != 0) { return new BERTaggedObjectParser(tag, tagNo, defIn); } if (isConstructed) { // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagNo) { case DERTags.OCTET_STRING: // // yes, people actually do this... // return new BEROctetStringParser(new ASN1StreamParser(defIn)); case DERTags.SEQUENCE: return new DERSequenceParser(new ASN1StreamParser(defIn)); case DERTags.SET: return new DERSetParser(new ASN1StreamParser(defIn)); case DERTags.EXTERNAL: return new DERExternalParser(new ASN1StreamParser(defIn)); default: // TODO Add DERUnknownTagParser class? return new DERUnknownTag(true, tagNo, defIn.toByteArray()); } } // Some primitive encodings can be handled by parsers too... switch (tagNo) { case DERTags.OCTET_STRING: return new DEROctetStringParser(defIn); } return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn.toByteArray()); } } private void set00Check(boolean enabled) { if (_in instanceof IndefiniteLengthInputStream) { ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); } } ASN1EncodableVector readVector() throws IOException { ASN1EncodableVector v = new ASN1EncodableVector(); DEREncodable obj; while ((obj = readObject()) != null) { v.add(obj.getDERObject()); } return v; } }