Java tutorial
package org.bouncycastle.asn1; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; /** * A parser for ASN.1 streams which also returns, where possible, parsers for the objects it encounters. */ public class ASN1StreamParser { private final InputStream _in; private final int _limit; private final byte[][] tmpBuffers; public ASN1StreamParser(InputStream in) { this(in, StreamUtil.findLimit(in)); } public ASN1StreamParser(InputStream in, int limit) { this._in = in; this._limit = limit; this.tmpBuffers = new byte[11][]; } public ASN1StreamParser(byte[] encoding) { this(new ByteArrayInputStream(encoding), encoding.length); } ASN1Encodable readIndef(int tagValue) throws IOException { // Note: INDEF => CONSTRUCTED // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagValue) { case BERTags.EXTERNAL: return new DERExternalParser(this); case BERTags.OCTET_STRING: return new BEROctetStringParser(this); case BERTags.SEQUENCE: return new BERSequenceParser(this); case BERTags.SET: return new BERSetParser(this); default: throw new ASN1Exception("unknown BER object encountered: 0x" + Integer.toHexString(tagValue)); } } ASN1Encodable readImplicit(boolean constructed, int tag) throws IOException { if (_in instanceof IndefiniteLengthInputStream) { if (!constructed) { throw new IOException("indefinite-length primitive encoding encountered"); } return readIndef(tag); } if (constructed) { switch (tag) { case BERTags.SET: return new DLSetParser(this); case BERTags.SEQUENCE: return new DLSequenceParser(this); case BERTags.OCTET_STRING: return new BEROctetStringParser(this); } } else { switch (tag) { case BERTags.SET: throw new ASN1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)"); case BERTags.SEQUENCE: throw new ASN1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)"); case BERTags.OCTET_STRING: return new DEROctetStringParser((DefiniteLengthInputStream) _in); } } throw new ASN1Exception("implicit tagging not implemented"); } ASN1Primitive readTaggedObject(boolean constructed, int tag) throws IOException { if (!constructed) { // Note: !CONSTRUCTED => IMPLICIT DefiniteLengthInputStream defIn = (DefiniteLengthInputStream) _in; return new DLTaggedObject(false, tag, new DEROctetString(defIn.toByteArray())); } ASN1EncodableVector v = readVector(); if (_in instanceof IndefiniteLengthInputStream) { return v.size() == 1 ? new BERTaggedObject(true, tag, v.get(0)) : new BERTaggedObject(false, tag, BERFactory.createSequence(v)); } return v.size() == 1 ? new DLTaggedObject(true, tag, v.get(0)) : new DLTaggedObject(false, tag, DLFactory.createSequence(v)); } public ASN1Encodable 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 & BERTags.CONSTRUCTED) != 0; // // calculate length // int length = ASN1InputStream.readLength(_in, _limit, tagNo == BERTags.OCTET_STRING || tagNo == BERTags.SEQUENCE || tagNo == BERTags.SET || tagNo == BERTags.EXTERNAL); if (length < 0) // indefinite-length method { if (!isConstructed) { throw new IOException("indefinite-length primitive encoding encountered"); } IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit); ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); if ((tag & BERTags.APPLICATION) != 0) { return new BERApplicationSpecificParser(tagNo, sp); } if ((tag & BERTags.TAGGED) != 0) { return new BERTaggedObjectParser(true, tagNo, sp); } return sp.readIndef(tagNo); } else { DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit); if ((tag & BERTags.APPLICATION) != 0) { return new DLApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); } if ((tag & BERTags.TAGGED) != 0) { return new BERTaggedObjectParser(isConstructed, tagNo, new ASN1StreamParser(defIn)); } if (isConstructed) { // TODO There are other tags that may be constructed (e.g. BIT_STRING) switch (tagNo) { case BERTags.OCTET_STRING: // // yes, people actually do this... // return new BEROctetStringParser(new ASN1StreamParser(defIn)); case BERTags.SEQUENCE: return new DLSequenceParser(new ASN1StreamParser(defIn)); case BERTags.SET: return new DLSetParser(new ASN1StreamParser(defIn)); case BERTags.EXTERNAL: return new DERExternalParser(new ASN1StreamParser(defIn)); default: throw new IOException("unknown tag " + tagNo + " encountered"); } } // Some primitive encodings can be handled by parsers too... switch (tagNo) { case BERTags.OCTET_STRING: return new DEROctetStringParser(defIn); } try { return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn, tmpBuffers); } catch (IllegalArgumentException e) { throw new ASN1Exception("corrupted stream detected", e); } } } private void set00Check(boolean enabled) { if (_in instanceof IndefiniteLengthInputStream) { ((IndefiniteLengthInputStream) _in).setEofOn00(enabled); } } ASN1EncodableVector readVector() throws IOException { ASN1Encodable obj = readObject(); if (null == obj) { return new ASN1EncodableVector(0); } ASN1EncodableVector v = new ASN1EncodableVector(); do { if (obj instanceof InMemoryRepresentable) { v.add(((InMemoryRepresentable) obj).getLoadedObject()); } else { v.add(obj.toASN1Primitive()); } } while ((obj = readObject()) != null); return v; } }