summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-18 16:31:12 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-18 16:31:12 +0700
commit1ec8938ad4d5810c3f50957b85c7fe9a1e6a00b7 (patch)
tree7760d701bc4f114be2b18f02896cbfa0442f958b
parentASN.1: Port of bc-java TYPE instances (diff)
downloadBouncyCastle.NET-ed25519-1ec8938ad4d5810c3f50957b85c7fe9a1e6a00b7.tar.xz
ASN:1 tagged object parser updates from bc-java
-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/ASN1OctetStringParser.cs5
-rw-r--r--crypto/src/asn1/ASN1TaggedObjectParser.cs22
-rw-r--r--crypto/src/asn1/Asn1BitStringParser.cs32
-rw-r--r--crypto/src/asn1/Asn1TaggedObject.cs39
-rw-r--r--crypto/src/asn1/Asn1Utilities.cs133
-rw-r--r--crypto/src/asn1/BERTaggedObjectParser.cs49
-rw-r--r--crypto/src/asn1/BerApplicationSpecificParser.cs20
-rw-r--r--crypto/src/asn1/DerApplicationSpecific.cs48
-rw-r--r--crypto/src/asn1/DerBitString.cs23
-rw-r--r--crypto/src/asn1/IAsn1ApplicationSpecificParser.cs5
14 files changed, 337 insertions, 47 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index c13fd13aa..cdc4843bd 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -63,6 +63,7 @@
     <Compile Include="src\asn1\ASN1SetParser.cs" />
     <Compile Include="src\asn1\ASN1StreamParser.cs" />
     <Compile Include="src\asn1\ASN1TaggedObjectParser.cs" />
+    <Compile Include="src\asn1\Asn1BitStringParser.cs" />
     <Compile Include="src\asn1\Asn1Encodable.cs" />
     <Compile Include="src\asn1\Asn1EncodableVector.cs" />
     <Compile Include="src\asn1\Asn1Exception.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index 9745dca60..bbcfd9324 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -57,6 +57,7 @@
     <Compile Include="src\asn1\ASN1SetParser.cs" />
     <Compile Include="src\asn1\ASN1StreamParser.cs" />
     <Compile Include="src\asn1\ASN1TaggedObjectParser.cs" />
+    <Compile Include="src\asn1\Asn1BitStringParser.cs" />
     <Compile Include="src\asn1\Asn1Encodable.cs" />
     <Compile Include="src\asn1\Asn1EncodableVector.cs" />
     <Compile Include="src\asn1\Asn1Exception.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index f718b9238..415992207 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -58,6 +58,7 @@
     <Compile Include="src\asn1\ASN1SetParser.cs" />
     <Compile Include="src\asn1\ASN1StreamParser.cs" />
     <Compile Include="src\asn1\ASN1TaggedObjectParser.cs" />
+    <Compile Include="src\asn1\Asn1BitStringParser.cs" />
     <Compile Include="src\asn1\Asn1Encodable.cs" />
     <Compile Include="src\asn1\Asn1EncodableVector.cs" />
     <Compile Include="src\asn1\Asn1Exception.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 69a44f9dc..72fcfe5b9 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -144,6 +144,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\Asn1BitStringParser.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\ASN1Encodable.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/ASN1OctetStringParser.cs b/crypto/src/asn1/ASN1OctetStringParser.cs
index 5815aa42f..07a3ac909 100644
--- a/crypto/src/asn1/ASN1OctetStringParser.cs
+++ b/crypto/src/asn1/ASN1OctetStringParser.cs
@@ -1,3 +1,4 @@
+using System;
 using System.IO;
 
 namespace Org.BouncyCastle.Asn1
@@ -5,6 +6,8 @@ namespace Org.BouncyCastle.Asn1
 	public interface Asn1OctetStringParser
 		: IAsn1Convertible
 	{
-		Stream GetOctetStream();
+        /// <summary>Return the content of the OCTET STRING as a <see cref="Stream"/>.</summary>
+        /// <returns>A <see cref="Stream"/> represnting the OCTET STRING's content.</returns>
+        Stream GetOctetStream();
 	}
 }
diff --git a/crypto/src/asn1/ASN1TaggedObjectParser.cs b/crypto/src/asn1/ASN1TaggedObjectParser.cs
index dd031ad07..d125ddc1a 100644
--- a/crypto/src/asn1/ASN1TaggedObjectParser.cs
+++ b/crypto/src/asn1/ASN1TaggedObjectParser.cs
@@ -1,3 +1,6 @@
+using System;
+using System.IO;
+
 namespace Org.BouncyCastle.Asn1
 {
 	public interface Asn1TaggedObjectParser
@@ -7,6 +10,21 @@ namespace Org.BouncyCastle.Asn1
 
 		int TagNo { get; }
 
-		IAsn1Convertible GetObjectParser(int tag, bool isExplicit);
-	}
+        bool HasContextTag(int tagNo);
+
+        bool HasTag(int tagClass, int tagNo);
+
+        /// <exception cref="IOException"/>
+        [Obsolete("Use 'Parse...' methods instead, after checking this parser's TagClass and TagNo")]
+        IAsn1Convertible GetObjectParser(int tag, bool isExplicit);
+
+        /// <exception cref="IOException"/>
+        IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo);
+
+        /// <exception cref="IOException"/>
+        Asn1TaggedObjectParser ParseExplicitBaseTagged();
+
+        /// <exception cref="IOException"/>
+        Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo);
+    }
 }
diff --git a/crypto/src/asn1/Asn1BitStringParser.cs b/crypto/src/asn1/Asn1BitStringParser.cs
new file mode 100644
index 000000000..76af06d73
--- /dev/null
+++ b/crypto/src/asn1/Asn1BitStringParser.cs
@@ -0,0 +1,32 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Asn1
+{
+    public interface Asn1BitStringParser
+        : IAsn1Convertible
+    {
+        /// <summary>Return a <see cref="Stream"/> representing the contents of the BIT STRING. The final byte, if any,
+        /// may include pad bits. See <see cref="PadBits"/>.</summary>
+        /// <returns>A <see cref="Stream"/> with its source as the BIT STRING content.</returns>
+        /// <exception cref="IOException"/>
+        Stream GetBitStream();
+
+        /// <summary>Return a <see cref="Stream"/> representing the contents of the BIT STRING, where the content is
+        /// expected to be octet-aligned (this will be automatically checked during parsing).</summary>
+        /// <returns>A <see cref="Stream"/> with its source as the BIT STRING content.</returns>
+        /// <exception cref="IOException"/>
+        Stream GetOctetStream();
+
+        /// <summary>Return the number of pad bits, if any, in the final byte, if any, read from
+        /// <see cref="GetBitStream"/>.</summary>
+        /// <remarks>
+        /// This number is in the range zero to seven. That number of the least significant bits of the final byte, if
+        /// any, are not part of the contents and should be ignored. NOTE: Must be called AFTER the stream has been
+        /// fully processed. (Does not need to be called if <see cref="GetOctetStream"/> was used instead of
+        /// <see cref="GetBitStream"/>.
+        /// </remarks>
+        /// <returns>The number of pad bits. In the range zero to seven.</returns>
+        int PadBits { get; }
+    }
+}
diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs
index 66835ac14..c7c0db6fc 100644
--- a/crypto/src/asn1/Asn1TaggedObject.cs
+++ b/crypto/src/asn1/Asn1TaggedObject.cs
@@ -368,28 +368,43 @@ namespace Org.BouncyCastle.Asn1
 		* the type of the passed in tag. If the object doesn't have a parser
 		* associated with it, the base object is returned.
 		*/
+        [Obsolete("Use 'Parse...' methods instead, after checking this parser's TagClass and TagNo")]
         public IAsn1Convertible GetObjectParser(int tag, bool isExplicit)
 		{
             if (Asn1Tags.ContextSpecific != TagClass)
                 throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags");
 
-            switch (tag)
-			{
-            //case Asn1Tags.BitString:
-            //    return Asn1BitString.GetInstance(this, isExplicit).Parser;
+            return ParseBaseUniversal(isExplicit, tag);
+		}
+
+        public IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo)
+        {
+            Asn1Object asn1Object = GetBaseUniversal(declaredExplicit, baseTagNo);
+
+            switch (baseTagNo)
+            {
+            case Asn1Tags.BitString:
+                return ((DerBitString)asn1Object).Parser;
             case Asn1Tags.OctetString:
-                return Asn1OctetString.GetInstance(this, isExplicit).Parser;
+                return ((Asn1OctetString)asn1Object).Parser;
             case Asn1Tags.Sequence:
-                return Asn1Sequence.GetInstance(this, isExplicit).Parser;
+                return ((Asn1Sequence)asn1Object).Parser;
             case Asn1Tags.Set:
-				return Asn1Set.GetInstance(this, isExplicit).Parser;
-			}
+                return ((Asn1Set)asn1Object).Parser;
+            }
 
-            if (isExplicit)
-                return obj.ToAsn1Object();
+            return asn1Object;
+        }
 
-			throw Platform.CreateNotImplementedException("implicit tagging for tag: " + tag);
-		}
+        public Asn1TaggedObjectParser ParseExplicitBaseTagged()
+        {
+            return GetExplicitBaseTagged();
+        }
+
+        public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo)
+        {
+            return GetImplicitBaseTagged(baseTagClass, baseTagNo);
+        }
 
 		public override string ToString()
 		{
diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs
index 7b014ef00..8a6d931c0 100644
--- a/crypto/src/asn1/Asn1Utilities.cs
+++ b/crypto/src/asn1/Asn1Utilities.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 namespace Org.BouncyCastle.Asn1
 {
@@ -15,6 +16,18 @@ namespace Org.BouncyCastle.Asn1
             return taggedObject;
         }
 
+        internal static Asn1TaggedObjectParser CheckTag(Asn1TaggedObjectParser taggedObjectParser, int tagClass,
+            int tagNo)
+        {
+            if (!taggedObjectParser.HasTag(tagClass, tagNo))
+            {
+                string expected = GetTagText(tagClass, tagNo);
+                string found = GetTagText(taggedObjectParser);
+                throw new InvalidOperationException("Expected " + expected + " tag but found " + found);
+            }
+            return taggedObjectParser;
+        }
+
 
         internal static string GetTagText(Asn1Tag tag)
         {
@@ -26,6 +39,11 @@ namespace Org.BouncyCastle.Asn1
             return GetTagText(taggedObject.TagClass, taggedObject.TagNo);
         }
 
+        public static string GetTagText(Asn1TaggedObjectParser taggedObjectParser)
+        {
+            return GetTagText(taggedObjectParser.TagClass, taggedObjectParser.TagNo);
+        }
+
         public static string GetTagText(int tagClass, int tagNo)
         {
             switch (tagClass)
@@ -118,9 +136,7 @@ namespace Org.BouncyCastle.Asn1
             int baseTagClass, int baseTagNo)
         {
             if (!taggedObject.HasTag(tagClass, tagNo))
-            {
                 return null;
-            }
 
             return taggedObject.GetImplicitBaseTagged(baseTagClass, baseTagNo);
         }
@@ -152,9 +168,7 @@ namespace Org.BouncyCastle.Asn1
             bool declaredExplicit, int baseTagNo)
         {
             if (!taggedObject.HasTag(tagClass, tagNo))
-            {
                 return null;
-            }
 
             return taggedObject.GetBaseUniversal(declaredExplicit, baseTagNo);
         }
@@ -164,5 +178,116 @@ namespace Org.BouncyCastle.Asn1
         {
             return TryGetBaseUniversal(taggedObject, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo);
         }
+
+
+        /*
+         * Wrappers for Asn1TaggedObjectParser.ParseExplicitBaseTagged
+         */
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser ParseExplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser,
+            int tagClass, int tagNo)
+        {
+            return CheckTag(taggedObjectParser, tagClass, tagNo).ParseExplicitBaseTagged();
+        }
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser ParseExplicitContextBaseTagged(Asn1TaggedObjectParser taggedObjectParser,
+            int tagNo)
+        {
+            return ParseExplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser TryParseExplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser,
+            int tagClass, int tagNo)
+        {
+            if (!taggedObjectParser.HasTag(tagClass, tagNo))
+                return null;
+
+            return taggedObjectParser.ParseExplicitBaseTagged();
+        }
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser TryParseExplicitContextBaseTagged(
+            Asn1TaggedObjectParser taggedObjectParser, int tagNo)
+        {
+            return TryParseExplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo);
+        }
+
+
+        /*
+         * Wrappers for Asn1TaggedObjectParser.ParseImplicitBaseTagged
+         */
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser ParseImplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser,
+            int tagClass, int tagNo, int baseTagClass, int baseTagNo)
+        {
+            return CheckTag(taggedObjectParser, tagClass, tagNo).ParseImplicitBaseTagged(baseTagClass, baseTagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser ParseImplicitContextBaseTagged(Asn1TaggedObjectParser taggedObjectParser,
+            int tagNo, int baseTagClass, int baseTagNo)
+        {
+            return ParseImplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, baseTagClass,
+                baseTagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser TryParseImplicitBaseTagged(Asn1TaggedObjectParser taggedObjectParser,
+            int tagClass, int tagNo, int baseTagClass, int baseTagNo)
+        {
+            if (!taggedObjectParser.HasTag(tagClass, tagNo))
+                return null;
+
+            return taggedObjectParser.ParseImplicitBaseTagged(baseTagClass, baseTagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static Asn1TaggedObjectParser TryParseImplicitContextBaseTagged(
+            Asn1TaggedObjectParser taggedObjectParser, int tagNo, int baseTagClass, int baseTagNo)
+        {
+            return TryParseImplicitBaseTagged(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, baseTagClass,
+                baseTagNo);
+        }
+
+
+        /*
+         * Wrappers for Asn1TaggedObjectParser.ParseBaseUniversal
+         */
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible ParseBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, int tagClass,
+            int tagNo, bool declaredExplicit, int baseTagNo)
+        {
+            return CheckTag(taggedObjectParser, tagClass, tagNo).ParseBaseUniversal(declaredExplicit, baseTagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible ParseContextBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, int tagNo,
+            bool declaredExplicit, int baseTagNo)
+        {
+            return ParseBaseUniversal(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible TryParseBaseUniversal(Asn1TaggedObjectParser taggedObjectParser, int tagClass,
+            int tagNo, bool declaredExplicit, int baseTagNo)
+        {
+            if (!taggedObjectParser.HasTag(tagClass, tagNo))
+                return null;
+
+            return taggedObjectParser.ParseBaseUniversal(declaredExplicit, baseTagNo);
+        }
+
+        /// <exception cref="IOException"/>
+        public static IAsn1Convertible TryParseContextBaseUniversal(Asn1TaggedObjectParser taggedObjectParser,
+            int tagNo, bool declaredExplicit, int baseTagNo)
+        {
+            return TryParseBaseUniversal(taggedObjectParser, Asn1Tags.ContextSpecific, tagNo, declaredExplicit,
+                baseTagNo);
+        }
     }
 }
diff --git a/crypto/src/asn1/BERTaggedObjectParser.cs b/crypto/src/asn1/BERTaggedObjectParser.cs
index 55ad36d46..a54e3ce38 100644
--- a/crypto/src/asn1/BERTaggedObjectParser.cs
+++ b/crypto/src/asn1/BERTaggedObjectParser.cs
@@ -34,21 +34,54 @@ namespace Org.BouncyCastle.Asn1
 			get { return m_tagNo; }
 		}
 
-		public IAsn1Convertible GetObjectParser(int baseTagNo, bool declaredExplicit)
+        public bool HasContextTag(int tagNo)
+        {
+            return m_tagClass == Asn1Tags.ContextSpecific && m_tagNo == tagNo;
+        }
+
+        public bool HasTag(int tagClass, int tagNo)
+        {
+            return m_tagClass == tagClass && m_tagNo == tagNo;
+        }
+
+        [Obsolete("Use 'Parse...' methods instead, after checking this parser's TagClass and TagNo")]
+        public IAsn1Convertible GetObjectParser(int baseTagNo, bool declaredExplicit)
 		{
             if (Asn1Tags.ContextSpecific != TagClass)
                 throw new Asn1Exception("this method only valid for CONTEXT_SPECIFIC tags");
 
-            if (!declaredExplicit)
-                return m_parser.ReadImplicit(m_constructed, baseTagNo);
+            return ParseBaseUniversal(declaredExplicit, baseTagNo);
+		}
 
-			if (!m_constructed)
-				throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+        public 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.ReadObject();
-		}
+                // TODO[asn1] Alternate parser method specific to this case
+                return m_parser.ReadObject();
+            }
+
+            return m_parser.ReadImplicit(m_constructed, baseTagNo);
+        }
+
+        public Asn1TaggedObjectParser ParseExplicitBaseTagged()
+        {
+            if (!m_constructed)
+                throw new IOException("Explicit tags must be constructed (see X.690 8.14.2)");
+
+            // TODO[asn1] Alternate parser method specific to this case
+            return (Asn1TaggedObjectParser)m_parser.ReadObject();
+        }
+
+        public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo)
+        {
+            return new BerTaggedObjectParser(baseTagClass, baseTagNo, m_constructed, m_parser);
+        }
 
-		public Asn1Object ToAsn1Object()
+        public Asn1Object ToAsn1Object()
 		{
 			try
 			{
diff --git a/crypto/src/asn1/BerApplicationSpecificParser.cs b/crypto/src/asn1/BerApplicationSpecificParser.cs
index 7d2c4b3e8..899e76ca5 100644
--- a/crypto/src/asn1/BerApplicationSpecificParser.cs
+++ b/crypto/src/asn1/BerApplicationSpecificParser.cs
@@ -3,27 +3,17 @@ using System;
 namespace Org.BouncyCastle.Asn1
 {
 	public class BerApplicationSpecificParser
-		: IAsn1ApplicationSpecificParser
+		: BerTaggedObjectParser, IAsn1ApplicationSpecificParser
 	{
-		private readonly int tag;
-		private readonly Asn1StreamParser parser;
-
-		internal BerApplicationSpecificParser(
-			int					tag,
-			Asn1StreamParser	parser)
+		internal BerApplicationSpecificParser(int tagNo, Asn1StreamParser parser)
+            : base(Asn1Tags.Application, tagNo, true, parser)
 		{
-			this.tag = tag;
-			this.parser = parser;
 		}
 
 		public IAsn1Convertible ReadObject()
 		{
-			return parser.ReadObject();
-		}
-
-		public Asn1Object ToAsn1Object()
-		{
-			return new BerApplicationSpecific(tag, parser.ReadVector());
+            // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser
+            return ParseBaseUniversal(true, -1);
 		}
 	}
 }
diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs
index 15a4cdcc2..92443b917 100644
--- a/crypto/src/asn1/DerApplicationSpecific.cs
+++ b/crypto/src/asn1/DerApplicationSpecific.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Asn1
      * Base class for an application specific object
      */
     public class DerApplicationSpecific
-        : Asn1Object
+        : Asn1Object, IAsn1ApplicationSpecificParser
     {
         public static DerApplicationSpecific GetInstance(object obj)
         {
@@ -116,17 +116,63 @@ namespace Org.BouncyCastle.Asn1
             return m_taggedObject.GetBaseUniversal(false, tagNo);
         }
 
+        public IAsn1Convertible GetObjectParser(int tag, bool isExplicit)
+        {
+            throw new Asn1Exception("this method only valid for CONTEXT_SPECIFIC tags");
+        }
+
+        public IAsn1Convertible ParseBaseUniversal(bool declaredExplicit, int baseTagNo)
+        {
+            return m_taggedObject.ParseBaseUniversal(declaredExplicit, baseTagNo);
+        }
+
+        public Asn1TaggedObjectParser ParseExplicitBaseTagged()
+        {
+            return m_taggedObject.ParseExplicitBaseTagged();
+        }
+
+        public Asn1TaggedObjectParser ParseImplicitBaseTagged(int baseTagClass, int baseTagNo)
+        {
+            return m_taggedObject.ParseImplicitBaseTagged(baseTagClass, baseTagNo);
+        }
+
         public bool HasApplicationTag(int tagNo)
         {
             return m_taggedObject.HasTag(Asn1Tags.Application, tagNo);
         }
 
+        public bool HasContextTag(int tagNo)
+        {
+            return false;
+        }
+
+        public bool HasTag(int tagClass, int tagNo)
+        {
+            return m_taggedObject.HasTag(tagClass, tagNo);
+        }
+
         [Obsolete("Will be removed")]
         public bool IsConstructed()
         {
             return m_taggedObject.IsConstructed();
         }
 
+        public IAsn1Convertible ReadObject()
+        {
+            // NOTE: No way to say you're looking for an implicitly-tagged object via IAsn1ApplicationSpecificParser
+            return m_taggedObject.GetBaseObject();
+        }
+
+        public int TagClass
+        {
+            get { return m_taggedObject.TagClass; }
+        }
+
+        public int TagNo
+        {
+            get { return m_taggedObject.TagNo; }
+        }
+
         /**
          * DerApplicationSpecific uses an internal Asn1TaggedObject for the
          * implementation, and will soon be deprecated in favour of using
diff --git a/crypto/src/asn1/DerBitString.cs b/crypto/src/asn1/DerBitString.cs
index 4596fbbc8..d3836b740 100644
--- a/crypto/src/asn1/DerBitString.cs
+++ b/crypto/src/asn1/DerBitString.cs
@@ -9,8 +9,8 @@ using Org.BouncyCastle.Utilities;
 namespace Org.BouncyCastle.Asn1
 {
 	public class DerBitString
-		: DerStringBase
-	{
+		: DerStringBase, Asn1BitStringParser
+    {
         internal class Meta : Asn1UniversalType
         {
             internal static readonly Asn1UniversalType Instance = new Meta();
@@ -309,6 +309,25 @@ namespace Org.BouncyCastle.Asn1
             return thisLastDer == thatLastDer;
         }
 
+        public Stream GetBitStream()
+        {
+            return new MemoryStream(contents, 1, contents.Length - 1, false);
+        }
+
+        public Stream GetOctetStream()
+        {
+            int padBits = contents[0] & 0xFF;
+            if (0 != padBits)
+                throw new IOException("expected octet-aligned bitstring, but found padBits: " + padBits);
+
+            return GetBitStream();
+        }
+
+        public Asn1BitStringParser Parser
+        {
+            get { return this; }
+        }
+
         public override string GetString()
 		{
 			byte[] str = GetDerEncoded();
diff --git a/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs b/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs
index 89cf64c70..f8dec2791 100644
--- a/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs
+++ b/crypto/src/asn1/IAsn1ApplicationSpecificParser.cs
@@ -2,9 +2,10 @@ using System;
 
 namespace Org.BouncyCastle.Asn1
 {
+    [Obsolete("Test for Asn1TaggedObjectParser with TagClass of Asn1Tags.Application instead")]
 	public interface IAsn1ApplicationSpecificParser
-    	: IAsn1Convertible
-	{
+    	: Asn1TaggedObjectParser
+    {
     	IAsn1Convertible ReadObject();
 	}
 }