summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-20 18:44:07 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-20 18:44:07 +0700
commitfe0abc8e4f4eb407a7fff3e5d567a51ce49a4baf (patch)
tree33f1beb1702c99430a033cd52b568b283319c487
parentASN:1 tagged object parser updates from bc-java (diff)
downloadBouncyCastle.NET-ed25519-fe0abc8e4f4eb407a7fff3e5d567a51ce49a4baf.tar.xz
ASN.1: Tagged object parser improvements
- add DLTaggedObjectParser
- refactoring of Asn1InputStream/Asn1StreamParser
-rw-r--r--crypto/BouncyCastle.Android.csproj1
-rw-r--r--crypto/BouncyCastle.csproj1
-rw-r--r--crypto/BouncyCastle.iOS.csproj1
-rw-r--r--crypto/crypto.csproj5
-rw-r--r--crypto/src/asn1/ASN1StreamParser.cs289
-rw-r--r--crypto/src/asn1/ASN1TaggedObjectParser.cs9
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs25
-rw-r--r--crypto/src/asn1/Asn1TaggedObject.cs56
-rw-r--r--crypto/src/asn1/Asn1Tags.cs2
-rw-r--r--crypto/src/asn1/Asn1Utilities.cs36
-rw-r--r--crypto/src/asn1/BERTaggedObjectParser.cs58
-rw-r--r--crypto/src/asn1/BerApplicationSpecificParser.cs6
-rw-r--r--crypto/src/asn1/DLTaggedObjectParser.cs81
-rw-r--r--crypto/src/asn1/DerApplicationSpecific.cs7
-rw-r--r--crypto/src/asn1/cms/ContentInfoParser.cs24
-rw-r--r--crypto/test/src/asn1/test/InputStreamTest.cs2
16 files changed, 390 insertions, 213 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index cdc4843bd..198ec093e 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -144,6 +144,7 @@
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
+    <Compile Include="src\asn1\DLTaggedObjectParser.cs" />
     <Compile Include="src\asn1\IAsn1ApplicationSpecificParser.cs" />
     <Compile Include="src\asn1\IAsn1Choice.cs" />
     <Compile Include="src\asn1\IAsn1Convertible.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index bbcfd9324..849dd3202 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -138,6 +138,7 @@
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
+    <Compile Include="src\asn1\DLTaggedObjectParser.cs" />
     <Compile Include="src\asn1\IAsn1ApplicationSpecificParser.cs" />
     <Compile Include="src\asn1\IAsn1Choice.cs" />
     <Compile Include="src\asn1\IAsn1Convertible.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 415992207..456a15b19 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -139,6 +139,7 @@
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
+    <Compile Include="src\asn1\DLTaggedObjectParser.cs" />
     <Compile Include="src\asn1\IAsn1ApplicationSpecificParser.cs" />
     <Compile Include="src\asn1\IAsn1Choice.cs" />
     <Compile Include="src\asn1\IAsn1Convertible.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 72fcfe5b9..a588a961a 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -579,6 +579,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\DLTaggedObjectParser.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\IAsn1ApplicationSpecificParser.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/ASN1StreamParser.cs b/crypto/src/asn1/ASN1StreamParser.cs
index 51af50728..6128c9b78 100644
--- a/crypto/src/asn1/ASN1StreamParser.cs
+++ b/crypto/src/asn1/ASN1StreamParser.cs
@@ -35,105 +35,36 @@ namespace Org.BouncyCastle.Asn1
             this.tmpBuffers = tmpBuffers;
         }
 
-        internal IAsn1Convertible ReadIndef(int tagNo)
-		{
-			// Note: INDEF => CONSTRUCTED
-
-			// TODO There are other tags that may be constructed (e.g. BIT_STRING)
-			switch (tagNo)
-			{
-			case Asn1Tags.External:
-				return new DerExternalParser(this);
-			case Asn1Tags.OctetString:
-				return new BerOctetStringParser(this);
-			case Asn1Tags.Sequence:
-				return new BerSequenceParser(this);
-			case Asn1Tags.Set:
-				return new BerSetParser(this);
-			default:
-				throw new Asn1Exception("unknown BER object encountered: 0x" + tagNo.ToString("X"));
-			}
-		}
-
-		internal IAsn1Convertible ReadImplicit(bool constructed, int tagNo)
-		{
-			if (_in is IndefiniteLengthInputStream)
-			{
-				if (!constructed)
-					throw new IOException("indefinite-length primitive encoding encountered");
-
-				return ReadIndef(tagNo);
-			}
-
-			if (constructed)
-			{
-				switch (tagNo)
-				{
-				case Asn1Tags.Set:
-					return new DerSetParser(this);
-				case Asn1Tags.Sequence:
-					return new DerSequenceParser(this);
-				case Asn1Tags.OctetString:
-					return new BerOctetStringParser(this);
-				}
-			}
-			else
-			{
-				switch (tagNo)
-				{
-				case Asn1Tags.Set:
-					throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
-				case Asn1Tags.Sequence:
-					throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
-				case Asn1Tags.OctetString:
-					return new DerOctetStringParser((DefiniteLengthInputStream)_in);
-				}
-			}
-
-			throw new Asn1Exception("implicit tagging not implemented");
-		}
-
-		internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed)
-		{
-            if (!constructed)
-			{
-                // Note: !CONSTRUCTED => IMPLICIT
-                byte[] contentsOctets = ((DefiniteLengthInputStream)_in).ToArray();
-                return Asn1TaggedObject.CreatePrimitive(tagClass, tagNo, contentsOctets);
-			}
-
-            bool isIL = (_in is IndefiniteLengthInputStream);
-            Asn1EncodableVector contentsElements = ReadVector();
-            return Asn1TaggedObject.CreateConstructed(tagClass, tagNo, isIL, contentsElements);
-		}
-
 		public virtual IAsn1Convertible ReadObject()
 		{
 			int tagHdr = _in.ReadByte();
-			if (tagHdr == -1)
+			if (tagHdr < 0)
 				return null;
 
-			// turn off looking for "00" while we resolve the tag
-			Set00Check(false);
+            return ImplParseObject(tagHdr);
+        }
+
+        internal IAsn1Convertible ImplParseObject(int tagHdr)
+        {
+            // turn off looking for "00" while we resolve the tag
+            Set00Check(false);
 
 			//
 			// calculate tag number
 			//
 			int tagNo = Asn1InputStream.ReadTagNumber(_in, tagHdr);
 
-			bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0;
-
 			//
 			// calculate length
 			//
 			int length = Asn1InputStream.ReadLength(_in, _limit,
-                tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || tagNo == Asn1Tags.Set ||
-                tagNo == Asn1Tags.External);
+                tagNo == Asn1Tags.BitString || tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence ||
+                tagNo == Asn1Tags.Set || tagNo == Asn1Tags.External);
 
 			if (length < 0) // indefinite-length method
 			{
-				if (!isConstructed)
-					throw new IOException("indefinite-length primitive encoding encountered");
+                if (0 == (tagHdr & Asn1Tags.Constructed))
+                    throw new IOException("indefinite-length primitive encoding encountered");
 
                 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
                 Asn1StreamParser sp = new Asn1StreamParser(indIn, _limit, tmpBuffers);
@@ -144,86 +75,184 @@ namespace Org.BouncyCastle.Asn1
                     if (Asn1Tags.Application == tagClass)
                         return new BerApplicationSpecificParser(tagNo, sp);
 
-                    return new BerTaggedObjectParser(tagClass, tagNo, true, sp);
+                    return new BerTaggedObjectParser(tagClass, tagNo, sp);
                 }
 
-                return sp.ReadIndef(tagNo);
+                return sp.ParseImplicitConstructedIL(tagNo);
 			}
 			else
 			{
 				DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit);
 
+                if (0 == (tagHdr & Asn1Tags.Flags))
+                    return ParseImplicitPrimitive(tagNo, defIn);
+
+                Asn1StreamParser sp = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers);
+
                 int tagClass = tagHdr & Asn1Tags.Private;
                 if (0 != tagClass)
                 {
-                    Asn1StreamParser sub = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers);
+                    bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0;
 
+                    // TODO[asn1] Special handling can be removed once ASN1ApplicationSpecific types removed.
                     if (Asn1Tags.Application == tagClass)
-                        return (DLApplicationSpecific)sub.ReadTaggedObject(tagClass, tagNo, isConstructed);
-
-                    return new BerTaggedObjectParser(tagClass, tagNo, isConstructed, sub);
-                }
-
-                if (!isConstructed)
-                {
-                    // Some primitive encodings can be handled by parsers too...
-                    switch (tagNo)
                     {
-                    case Asn1Tags.OctetString:
-                        return new DerOctetStringParser(defIn);
+                        // This cast is ensuring the current user-expected return type.
+                        return (DLApplicationSpecific)sp.LoadTaggedDL(tagClass, tagNo, isConstructed);
                     }
 
-                    try
-                    {
-                        return Asn1InputStream.CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers);
-                    }
-                    catch (ArgumentException e)
-                    {
-                        throw new Asn1Exception("corrupted stream detected", e);
-                    }
+                    return new DLTaggedObjectParser(tagClass, tagNo, isConstructed, sp);
                 }
 
-                Asn1StreamParser sp = new Asn1StreamParser(defIn, defIn.Remaining, tmpBuffers);
-
-                // TODO There are other tags that may be constructed (e.g. BitString)
-                switch (tagNo)
-				{
-				case Asn1Tags.OctetString:
-					return new BerOctetStringParser(sp);
-				case Asn1Tags.Sequence:
-					return new DerSequenceParser(sp);
-				case Asn1Tags.Set:
-					return new DerSetParser(sp);
-				case Asn1Tags.External:
-					return new DerExternalParser(sp);
-				default:
-                    throw new IOException("unknown tag " + tagNo + " encountered");
-                }
+                return sp.ParseImplicitConstructedDL(tagNo);
 			}
 		}
 
-		private void Set00Check(
-			bool enabled)
-		{
-			if (_in is IndefiniteLengthInputStream)
-			{
-				((IndefiniteLengthInputStream) _in).SetEofOn00(enabled);
-			}
-		}
+        internal Asn1Object LoadTaggedDL(int tagClass, int tagNo, bool constructed)
+        {
+            if (!constructed)
+            {
+                byte[] contentsOctets = ((DefiniteLengthInputStream)_in).ToArray();
+                return Asn1TaggedObject.CreatePrimitive(tagClass, tagNo, contentsOctets);
+            }
+
+            Asn1EncodableVector contentsElements = ReadVector();
+            return Asn1TaggedObject.CreateConstructedDL(tagClass, tagNo, contentsElements);
+        }
+
+        internal Asn1Object LoadTaggedIL(int tagClass, int tagNo)
+        {
+            Asn1EncodableVector contentsElements = ReadVector();
+            return Asn1TaggedObject.CreateConstructedIL(tagClass, tagNo, contentsElements);
+        }
+
+        internal IAsn1Convertible ParseImplicitConstructedDL(int univTagNo)
+        {
+            switch (univTagNo)
+            {
+            // TODO[asn1] DLConstructedBitStringParser
+            //case Asn1Tags.BitString:
+            //    return new BerBitStringParser(this);
+            case Asn1Tags.External:
+                return new DerExternalParser(this);
+            case Asn1Tags.OctetString:
+                // TODO[asn1] DLConstructedOctetStringParser
+                return new BerOctetStringParser(this);
+            case Asn1Tags.Set:
+                return new DerSetParser(this);
+            case Asn1Tags.Sequence:
+                return new DerSequenceParser(this);
+            default:
+				throw new Asn1Exception("unknown DL object encountered: 0x" + univTagNo.ToString("X"));
+            }
+        }
+
+        internal IAsn1Convertible ParseImplicitConstructedIL(int univTagNo)
+        {
+            switch (univTagNo)
+            {
+            // TODO[asn1] BerBitStringParser
+            //case Asn1Tags.BitString:
+            //    return new BerBitStringParser(this);
+            case Asn1Tags.External:
+                // TODO[asn1] BERExternalParser
+                return new DerExternalParser(this);
+            case Asn1Tags.OctetString:
+                return new BerOctetStringParser(this);
+            case Asn1Tags.Sequence:
+                return new BerSequenceParser(this);
+            case Asn1Tags.Set:
+                return new BerSetParser(this);
+            default:
+                throw new Asn1Exception("unknown BER object encountered: 0x" + univTagNo.ToString("X"));
+            }
+        }
+
+        internal IAsn1Convertible ParseImplicitPrimitive(int univTagNo)
+        {
+            return ParseImplicitPrimitive(univTagNo, (DefiniteLengthInputStream)_in);
+        }
 
+        internal IAsn1Convertible ParseImplicitPrimitive(int univTagNo, DefiniteLengthInputStream defIn)
+        {
+            // Some primitive encodings can be handled by parsers too...
+            switch (univTagNo)
+            {
+            // TODO[asn1] DLBitStringParser
+            //case Asn1Tags.BitString:
+            //    return new DLBitStringParser(defIn);
+            case Asn1Tags.External:
+                throw new Asn1Exception("externals must use constructed encoding (see X.690 8.18)");
+            case Asn1Tags.OctetString:
+                return new DerOctetStringParser(defIn);
+			case Asn1Tags.Set:
+				throw new Asn1Exception("sequences must use constructed encoding (see X.690 8.9.1/8.10.1)");
+			case Asn1Tags.Sequence:
+				throw new Asn1Exception("sets must use constructed encoding (see X.690 8.11.1/8.12.1)");
+            }
+
+            try
+            {
+                return Asn1InputStream.CreatePrimitiveDerObject(univTagNo, defIn, tmpBuffers);
+            }
+            catch (ArgumentException e)
+            {
+                throw new Asn1Exception("corrupted stream detected", e);
+            }
+        }
+
+        internal IAsn1Convertible ParseObject(int univTagNo)
+        {
+            if (univTagNo < 0 || univTagNo > 30)
+                throw new ArgumentException("invalid universal tag number: " + univTagNo, "univTagNo");
+
+            int tagHdr = _in.ReadByte();
+            if (tagHdr < 0)
+                return null;
+
+            if ((tagHdr & ~Asn1Tags.Constructed) != univTagNo)
+                throw new IOException("unexpected identifier encountered: " + tagHdr);
+
+            return ImplParseObject(tagHdr);
+        }
+
+        internal Asn1TaggedObjectParser ParseTaggedObject()
+        {
+            int tagHdr = _in.ReadByte();
+            if (tagHdr< 0)
+                return null;
+
+            int tagClass = tagHdr & Asn1Tags.Private;
+            if (0 == tagClass)
+                throw new Asn1Exception("no tagged object found");
+
+            return (Asn1TaggedObjectParser)ImplParseObject(tagHdr);
+        }
+
+        // TODO[asn1] Prefer 'LoadVector'
         internal Asn1EncodableVector ReadVector()
         {
-            IAsn1Convertible obj = ReadObject();
-            if (null == obj)
+            int tagHdr = _in.ReadByte();
+            if (tagHdr < 0)
                 return new Asn1EncodableVector(0);
 
             Asn1EncodableVector v = new Asn1EncodableVector();
             do
             {
+                IAsn1Convertible obj = ImplParseObject(tagHdr);
+
                 v.Add(obj.ToAsn1Object());
             }
-            while ((obj = ReadObject()) != null);
+            while ((tagHdr = _in.ReadByte()) >= 0);
             return v;
         }
+
+		private void Set00Check(bool enabled)
+		{
+			if (_in is IndefiniteLengthInputStream)
+			{
+				((IndefiniteLengthInputStream)_in).SetEofOn00(enabled);
+			}
+		}
 	}
 }
diff --git a/crypto/src/asn1/ASN1TaggedObjectParser.cs b/crypto/src/asn1/ASN1TaggedObjectParser.cs
index d125ddc1a..68b83f290 100644
--- a/crypto/src/asn1/ASN1TaggedObjectParser.cs
+++ b/crypto/src/asn1/ASN1TaggedObjectParser.cs
@@ -21,6 +21,15 @@ namespace Org.BouncyCastle.Asn1
         /// <exception cref="IOException"/>
         IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo);
 
+        /// <summary>Needed for open types, until we have better type-guided parsing support.</summary>
+        /// <remarks>
+        /// Use sparingly for other purposes, and prefer <see cref="ParseExplicitBaseTagged"/> or
+        /// <see cref="ParseBaseUniversal(bool, int)"/> where possible. Before using, check for matching tag
+        /// <see cref="TagClass">class</see> and <see cref="TagNo">number</see>.
+        /// </remarks>
+        /// <exception cref="IOException"/>
+        IAsn1Convertible ParseExplicitBaseObject();
+
         /// <exception cref="IOException"/>
         Asn1TaggedObjectParser ParseExplicitBaseTagged();
 
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index d03c7a6b5..8ddfc022b 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -75,16 +75,19 @@ namespace Org.BouncyCastle.Asn1
         */
         private Asn1Object BuildObject(int tagHdr, int tagNo, int length)
         {
-            bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0;
+            // TODO[asn1] Special-case zero length first?
 
             DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length, limit);
 
+            if (0 == (tagHdr & Asn1Tags.Flags))
+                return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers);
+
             int tagClass = tagHdr & Asn1Tags.Private;
             if (0 != tagClass)
-                return ReadTaggedObject(tagClass, tagNo, isConstructed, defIn);
-
-            if (!isConstructed)
-                return CreatePrimitiveDerObject(tagNo, defIn, tmpBuffers);
+            {
+                bool isConstructed = (tagHdr & Asn1Tags.Constructed) != 0;
+                return ReadTaggedObjectDL(tagClass, tagNo, isConstructed, defIn);
+            }
 
             switch (tagNo)
             {
@@ -103,7 +106,7 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
-        internal Asn1Object ReadTaggedObject(int tagClass, int tagNo, bool constructed, DefiniteLengthInputStream defIn)
+        internal Asn1Object ReadTaggedObjectDL(int tagClass, int tagNo, bool constructed, DefiniteLengthInputStream defIn)
         {
             if (!constructed)
             {
@@ -111,9 +114,8 @@ namespace Org.BouncyCastle.Asn1
                 return Asn1TaggedObject.CreatePrimitive(tagClass, tagNo, contentsOctets);
             }
 
-            bool isIL = false;
             Asn1EncodableVector contentsElements = ReadVector(defIn);
-            return Asn1TaggedObject.CreateConstructed(tagClass, tagNo, isIL, contentsElements);
+            return Asn1TaggedObject.CreateConstructedDL(tagClass, tagNo, contentsElements);
         }
 
         internal virtual Asn1EncodableVector ReadVector()
@@ -187,11 +189,13 @@ namespace Org.BouncyCastle.Asn1
 
             int tagClass = tagHdr & Asn1Tags.Private;
             if (0 != tagClass)
-                return sp.ReadTaggedObject(tagClass, tagNo, true);
+                return sp.LoadTaggedIL(tagClass, tagNo);
 
-            // TODO There are other tags that may be constructed (e.g. BitString)
             switch (tagNo)
             {
+            // TODO[asn1] BerBitStringParser
+            //case Asn1Tags.BitString:
+            //    return BerBitStringParser.Parse(sp);
             case Asn1Tags.OctetString:
                 return BerOctetStringParser.Parse(sp);
             case Asn1Tags.Sequence:
@@ -199,6 +203,7 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.Set:
                 return BerSetParser.Parse(sp);
             case Asn1Tags.External:
+                // TODO[asn1] BerExternalParser
                 return DerExternalParser.Parse(sp);
             default:
                 throw new IOException("unknown BER object encountered");
diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs
index c7c0db6fc..ce5e9e3f2 100644
--- a/crypto/src/asn1/Asn1TaggedObject.cs
+++ b/crypto/src/asn1/Asn1TaggedObject.cs
@@ -396,6 +396,11 @@ namespace Org.BouncyCastle.Asn1
             return asn1Object;
         }
 
+        public IAsn1Convertible ParseExplicitBaseObject()
+        {
+            return GetExplicitBaseObject();
+        }
+
         public Asn1TaggedObjectParser ParseExplicitBaseTagged()
         {
             return GetExplicitBaseTagged();
@@ -417,38 +422,37 @@ namespace Org.BouncyCastle.Asn1
 
         internal abstract Asn1TaggedObject ReplaceTag(int tagClass, int tagNo);
 
-        internal static Asn1Object CreateConstructed(int tagClass, int tagNo, bool isIL,
-            Asn1EncodableVector contentsElements)
+        internal static Asn1Object CreateConstructedDL(int tagClass, int tagNo, Asn1EncodableVector contentsElements)
         {
             bool maybeExplicit = (contentsElements.Count == 1);
 
-            if (isIL)
-            {
-                Asn1TaggedObject taggedObject = maybeExplicit
-                    ?   new BerTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0])
-                    :   new BerTaggedObject(ParsedImplicit, tagClass, tagNo, BerSequence.FromVector(contentsElements));
+            Asn1TaggedObject taggedObject = maybeExplicit
+                ? new DLTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0])
+                : new DLTaggedObject(ParsedImplicit, tagClass, tagNo, DLSequence.FromVector(contentsElements));
 
-                switch (tagClass)
-                {
-                case Asn1Tags.Application:
-                    return new BerApplicationSpecific(taggedObject);
-                default:
-                    return taggedObject;
-                }
-            }
-            else
+            switch (tagClass)
             {
-                Asn1TaggedObject taggedObject = maybeExplicit
-                    ?   new DLTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0])
-                    :   new DLTaggedObject(ParsedImplicit, tagClass, tagNo, DLSequence.FromVector(contentsElements));
+            case Asn1Tags.Application:
+                return new DLApplicationSpecific(taggedObject);
+            default:
+                return taggedObject;
+            }
+        }
 
-                switch (tagClass)
-                {
-                case Asn1Tags.Application:
-                    return new DLApplicationSpecific(taggedObject);
-                default:
-                    return taggedObject;
-                }
+        internal static Asn1Object CreateConstructedIL(int tagClass, int tagNo, Asn1EncodableVector contentsElements)
+        {
+            bool maybeExplicit = (contentsElements.Count == 1);
+
+            Asn1TaggedObject taggedObject = maybeExplicit
+                ? new BerTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0])
+                : new BerTaggedObject(ParsedImplicit, tagClass, tagNo, BerSequence.FromVector(contentsElements));
+
+            switch (tagClass)
+            {
+            case Asn1Tags.Application:
+                return new BerApplicationSpecific(taggedObject);
+            default:
+                return taggedObject;
             }
         }
 
diff --git a/crypto/src/asn1/Asn1Tags.cs b/crypto/src/asn1/Asn1Tags.cs
index 692acdf50..670fb7d96 100644
--- a/crypto/src/asn1/Asn1Tags.cs
+++ b/crypto/src/asn1/Asn1Tags.cs
@@ -45,5 +45,7 @@ namespace Org.BouncyCastle.Asn1
         public const int Tagged = 0x80;
         public const int ContextSpecific = 0x80;
         public const int Private = 0xC0;
+
+        public const int Flags = 0xE0;
     }
 }
diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs
index 8a6d931c0..5605d4c54 100644
--- a/crypto/src/asn1/Asn1Utilities.cs
+++ b/crypto/src/asn1/Asn1Utilities.cs
@@ -289,5 +289,41 @@ namespace Org.BouncyCastle.Asn1
             return TryParseBaseUniversal(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, declaredExplicit,
                 baseTagNo);
         }
+
+
+        /*
+         * Wrappers for Asn1TaggedObjectParser.ParseExplicitBaseObject
+         */
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible ParseExplicitBaseObject(Asn1TaggedObjectParser taggedObjectParser, int tagClass,
+            int tagNo)
+        {
+            return CheckTag(taggedObjectParser, tagClass, tagNo).ParseExplicitBaseObject();
+        }
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible ParseExplicitContextBaseObject(Asn1TaggedObjectParser taggedObjectParser,
+            int tagNo)
+        {
+            return ParseExplicitBaseObject(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible TryParseExplicitBaseObject(Asn1TaggedObjectParser taggedObjectParser,
+            int tagClass, int tagNo)
+        {
+            if (!taggedObjectParser.HasTag(tagClass, tagNo))
+                return null;
+
+            return taggedObjectParser.ParseExplicitBaseObject();
+        }
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible TryParseExplicitContextBaseObject(Asn1TaggedObjectParser taggedObjectParser,
+            int tagNo)
+        {
+            return TryParseExplicitBaseObject(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo);
+        }
     }
 }
diff --git a/crypto/src/asn1/BERTaggedObjectParser.cs b/crypto/src/asn1/BERTaggedObjectParser.cs
index a54e3ce38..c87f6cf9a 100644
--- a/crypto/src/asn1/BERTaggedObjectParser.cs
+++ b/crypto/src/asn1/BERTaggedObjectParser.cs
@@ -3,25 +3,24 @@ using System.IO;
 
 namespace Org.BouncyCastle.Asn1
 {
+    [Obsolete("Will be made non-public. Test for and use only Asn1TaggedObjectParser.")]
 	public class BerTaggedObjectParser
 		: Asn1TaggedObjectParser
 	{
-        private readonly int m_tagClass;
-        private readonly int m_tagNo;
-        private readonly bool m_constructed;
-        private readonly Asn1StreamParser m_parser;
+        internal readonly int m_tagClass;
+        internal readonly int m_tagNo;
+        internal readonly Asn1StreamParser m_parser;
 
-		internal BerTaggedObjectParser(int tagClass, int tagNo, bool constructed, Asn1StreamParser parser)
+		internal BerTaggedObjectParser(int tagClass, int tagNo, Asn1StreamParser parser)
 		{
-            this.m_tagClass = tagClass;
-            this.m_tagNo = tagNo;
-            this.m_constructed = constructed;
-            this.m_parser = parser;
+            m_tagClass = tagClass;
+            m_tagNo = tagNo;
+            m_parser = parser;
 		}
 
-		public bool IsConstructed
+		public virtual bool IsConstructed
 		{
-			get { return m_constructed; }
+			get { return true; }
 		}
 
         public int TagClass
@@ -53,40 +52,39 @@ namespace Org.BouncyCastle.Asn1
             return ParseBaseUniversal(declaredExplicit, baseTagNo);
 		}
 
-        public IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo)
+        public virtual IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo)
         {
             if (declaredExplicit)
-            {
-                if (!m_constructed)
-                    throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+                return m_parser.ParseObject(baseTagNo);
 
-                // TODO[asn1] Alternate parser method specific to this case
-                return m_parser.ReadObject();
-            }
-
-            return m_parser.ReadImplicit(m_constructed, baseTagNo);
+            return m_parser.ParseImplicitConstructedIL(baseTagNo);
         }
 
-        public Asn1TaggedObjectParser ParseExplicitBaseTagged()
+        public virtual IAsn1Convertible ParseExplicitBaseObject()
         {
-            if (!m_constructed)
-                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+            return m_parser.ReadObject();
+        }
 
-            // TODO[asn1] Alternate parser method specific to this case
-            return (Asn1TaggedObjectParser)m_parser.ReadObject();
+        public virtual Asn1TaggedObjectParser ParseExplicitBaseTagged()
+        {
+            return m_parser.ParseTaggedObject();
         }
 
-        public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo)
+        public virtual Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo)
         {
-            return new BerTaggedObjectParser(baseTagClass, baseTagNo, m_constructed, m_parser);
+            // TODO[asn1] Special handling can be removed once ASN1ApplicationSpecificParser types removed.
+            if (Asn1Tags.Application == baseTagClass)
+                return new BerApplicationSpecificParser(baseTagNo, m_parser);
+
+            return new BerTaggedObjectParser(baseTagClass, baseTagNo, m_parser);
         }
 
-        public Asn1Object ToAsn1Object()
+        public virtual Asn1Object ToAsn1Object()
 		{
 			try
 			{
-				return m_parser.ReadTaggedObject(TagClass, TagNo, IsConstructed);
-			}
+                return m_parser.LoadTaggedIL(TagClass, TagNo);
+            }
 			catch (IOException e)
 			{
 				throw new Asn1ParsingException(e.Message);
diff --git a/crypto/src/asn1/BerApplicationSpecificParser.cs b/crypto/src/asn1/BerApplicationSpecificParser.cs
index 899e76ca5..75b7e7632 100644
--- a/crypto/src/asn1/BerApplicationSpecificParser.cs
+++ b/crypto/src/asn1/BerApplicationSpecificParser.cs
@@ -6,14 +6,14 @@ namespace Org.BouncyCastle.Asn1
 		: BerTaggedObjectParser, IAsn1ApplicationSpecificParser
 	{
 		internal BerApplicationSpecificParser(int tagNo, Asn1StreamParser parser)
-            : base(Asn1Tags.Application, tagNo, true, parser)
+            : base(Asn1Tags.Application, tagNo, parser)
 		{
 		}
 
 		public IAsn1Convertible ReadObject()
 		{
             // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser
-            return ParseBaseUniversal(true, -1);
-		}
+            return ParseExplicitBaseObject();
+        }
 	}
 }
diff --git a/crypto/src/asn1/DLTaggedObjectParser.cs b/crypto/src/asn1/DLTaggedObjectParser.cs
new file mode 100644
index 000000000..307a6342a
--- /dev/null
+++ b/crypto/src/asn1/DLTaggedObjectParser.cs
@@ -0,0 +1,81 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /**
+     * Parser for definite-length tagged objects.
+     */
+    internal class DLTaggedObjectParser
+        : BerTaggedObjectParser
+    {
+        private readonly bool m_constructed;
+
+        internal DLTaggedObjectParser(int tagClass, int tagNo, bool constructed, Asn1StreamParser parser)
+            : base(tagClass, tagNo, parser)
+        {
+            m_constructed = constructed;
+        }
+
+        public override bool IsConstructed
+        {
+            get { return m_constructed; }
+        }
+
+        public override IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo)
+        {
+            if (declaredExplicit)
+            {
+                if (!m_constructed)
+                    throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+
+                return m_parser.ParseObject(baseTagNo);
+            }
+
+            return m_constructed
+                ?  m_parser.ParseImplicitConstructedDL(baseTagNo)
+                :  m_parser.ParseImplicitPrimitive(baseTagNo);
+        }
+
+        public override IAsn1Convertible ParseExplicitBaseObject()
+        {
+            if (!m_constructed)
+                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+
+            return m_parser.ReadObject();
+        }
+
+        public override Asn1TaggedObjectParser ParseExplicitBaseTagged()
+        {
+            if (!m_constructed)
+                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+
+            return m_parser.ParseTaggedObject();
+        }
+
+        public override Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo)
+        {
+            // TODO[asn1] Special handling can be removed once ASN1ApplicationSpecificParser types removed.
+            if (Asn1Tags.Application == baseTagClass)
+            {
+                // This cast is ensuring the current user-expected return type.
+                return (DLApplicationSpecific)m_parser.LoadTaggedDL(baseTagClass, baseTagNo, m_constructed);
+            }
+
+            return new DLTaggedObjectParser(baseTagClass, baseTagNo, m_constructed, m_parser);
+        }
+
+        public override Asn1Object ToAsn1Object()
+		{
+			try
+			{
+                return m_parser.LoadTaggedDL(TagClass, TagNo, m_constructed);
+            }
+			catch (IOException e)
+			{
+				throw new Asn1ParsingException(e.Message);
+			}
+		}
+    }
+}
+
diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs
index 92443b917..d474034ed 100644
--- a/crypto/src/asn1/DerApplicationSpecific.cs
+++ b/crypto/src/asn1/DerApplicationSpecific.cs
@@ -126,6 +126,11 @@ namespace Org.BouncyCastle.Asn1
             return m_taggedObject.ParseBaseUniversal(declaredExplicit, baseTagNo);
         }
 
+        public IAsn1Convertible ParseExplicitBaseObject()
+        {
+            return TaggedObject.ParseExplicitBaseObject();
+        }
+
         public Asn1TaggedObjectParser ParseExplicitBaseTagged()
         {
             return m_taggedObject.ParseExplicitBaseTagged();
@@ -160,7 +165,7 @@ namespace Org.BouncyCastle.Asn1
         public IAsn1Convertible ReadObject()
         {
             // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser
-            return m_taggedObject.GetBaseObject();
+            return ParseExplicitBaseObject();
         }
 
         public int TagClass
diff --git a/crypto/src/asn1/cms/ContentInfoParser.cs b/crypto/src/asn1/cms/ContentInfoParser.cs
index 541cc0f59..750d8ba7a 100644
--- a/crypto/src/asn1/cms/ContentInfoParser.cs
+++ b/crypto/src/asn1/cms/ContentInfoParser.cs
@@ -13,28 +13,28 @@ namespace Org.BouncyCastle.Asn1.Cms
 	*/
 	public class ContentInfoParser
 	{
-		private DerObjectIdentifier		contentType;
-		private Asn1TaggedObjectParser	content;
+		private readonly DerObjectIdentifier m_contentType;
+		private readonly Asn1TaggedObjectParser m_content;
 
-		public ContentInfoParser(
-			Asn1SequenceParser seq)
+		public ContentInfoParser(Asn1SequenceParser seq)
 		{
-			contentType = (DerObjectIdentifier)seq.ReadObject();
-			content = (Asn1TaggedObjectParser)seq.ReadObject();
+			m_contentType = (DerObjectIdentifier)seq.ReadObject();
+			m_content = (Asn1TaggedObjectParser)seq.ReadObject();
 		}
 
 		public DerObjectIdentifier ContentType
 		{
-			get { return contentType; }
+			get { return m_contentType; }
 		}
 
-		public IAsn1Convertible GetContent(
-			int tag)
+		public IAsn1Convertible GetContent(int tag)
 		{
-			if (content == null)
+			if (null == m_content)
 				return null;
 
-			return content.GetObjectParser(tag, true);
-		}
+            // TODO[cms] Ideally we could enforce the claimed tag
+            //return Asn1Utilities.ParseContextBaseUniversal(content, 0, true, tag);
+            return Asn1Utilities.ParseExplicitContextBaseObject(m_content, 0);
+        }
 	}
 }
diff --git a/crypto/test/src/asn1/test/InputStreamTest.cs b/crypto/test/src/asn1/test/InputStreamTest.cs
index ab5200905..8f29b41ac 100644
--- a/crypto/test/src/asn1/test/InputStreamTest.cs
+++ b/crypto/test/src/asn1/test/InputStreamTest.cs
@@ -77,7 +77,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 
             // TODO Test data has length issues too; needs to be reworked
             //DoTestWithByteArray(classCast1, "unknown object encountered: Org.BouncyCastle.Asn1.DerApplicationSpecific");
-            DoTestWithByteArray(classCast2, "unknown object encountered: Org.BouncyCastle.Asn1.BerTaggedObjectParser");
+            DoTestWithByteArray(classCast2, "unknown object encountered: Org.BouncyCastle.Asn1.DLTaggedObjectParser");
             DoTestWithByteArray(classCast3, "unknown object encountered in constructed OCTET STRING: Org.BouncyCastle.Asn1.DLTaggedObject");
 
             // TODO Error dependent on parser choices; needs to be reworked