summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-12 01:54:30 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-12 01:54:30 +0700
commit8c32dba3fc4f2033e7597ee1999e793cf6552584 (patch)
treec48d45537cfbea6f7e0ab92de4793a2b35204573
parentAdd new Equals method (diff)
downloadBouncyCastle.NET-ed25519-8c32dba3fc4f2033e7597ee1999e793cf6552584.tar.xz
Reimplement application-specific over tagged object
-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/Asn1TaggedObject.cs229
-rw-r--r--crypto/src/asn1/BerApplicationSpecific.cs44
-rw-r--r--crypto/src/asn1/BerTaggedObject.cs57
-rw-r--r--crypto/src/asn1/DLApplicationSpecific.cs28
-rw-r--r--crypto/src/asn1/DLTaggedObject.cs23
-rw-r--r--crypto/src/asn1/DerApplicationSpecific.cs334
-rw-r--r--crypto/src/asn1/DerTaggedObject.cs55
-rw-r--r--crypto/src/asn1/util/Asn1Dump.cs92
12 files changed, 514 insertions, 356 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index 0cececb3f..c3d897efb 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -132,6 +132,7 @@
     <Compile Include="src\asn1\DerUniversalString.cs" />
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
+    <Compile Include="src\asn1\DLApplicationSpecific.cs" />
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index 2e82e2432..0ffedcb1a 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -126,6 +126,7 @@
     <Compile Include="src\asn1\DerUniversalString.cs" />
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
+    <Compile Include="src\asn1\DLApplicationSpecific.cs" />
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 29031b947..3cd17d407 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -127,6 +127,7 @@
     <Compile Include="src\asn1\DerUniversalString.cs" />
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
+    <Compile Include="src\asn1\DLApplicationSpecific.cs" />
     <Compile Include="src\asn1\DLSequence.cs" />
     <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\DLTaggedObject.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index b490065e0..870cafbe9 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -519,6 +519,11 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\DLApplicationSpecific.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\DLSequence.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs
index aaa719ecc..13d1ff283 100644
--- a/crypto/src/asn1/Asn1TaggedObject.cs
+++ b/crypto/src/asn1/Asn1TaggedObject.cs
@@ -13,18 +13,13 @@ namespace Org.BouncyCastle.Asn1
     public abstract class Asn1TaggedObject
 		: Asn1Object, Asn1TaggedObjectParser
     {
-        // TODO[asn1] Rewrite DerApplicationSpecific in terms of Asn1TaggedObject and remove this
-        internal static bool IsConstructed(bool isExplicit, Asn1Object obj)
-        {
-            if (isExplicit || obj is Asn1Sequence || obj is Asn1Set)
-                return true;
-            Asn1TaggedObject tagged = obj as Asn1TaggedObject;
-            if (tagged == null)
-                return false;
-            return IsConstructed(tagged.IsExplicit(), tagged.GetObject());
-        }
+        private const int DeclaredExplicit = 1;
+        private const int DeclaredImplicit = 2;
+        // TODO It will probably be better to track parsing constructed vs primitive instead
+        private const int ParsedExplicit = 3;
+        private const int ParsedImplicit = 4;
 
-		public static Asn1TaggedObject GetInstance(object obj)
+        public static Asn1TaggedObject GetInstance(object obj)
 		{
             if (obj == null || obj is Asn1TaggedObject) 
             {
@@ -57,69 +52,91 @@ namespace Org.BouncyCastle.Asn1
             if (Asn1Tags.ContextSpecific != taggedObject.TagClass)
                 throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags");
 
-            if (!declaredExplicit)
-                throw new ArgumentException("this method not valid for implicitly tagged tagged objects");
+            if (declaredExplicit)
+                return taggedObject.GetExplicitBaseTagged();
 
-            return taggedObject.GetExplicitBaseTagged();
+            throw new ArgumentException("this method not valid for implicitly tagged tagged objects");
         }
 
-        internal readonly int tagClass = Asn1Tags.ContextSpecific;
+        internal readonly int explicitness;
+        internal readonly int tagClass;
         internal readonly int tagNo;
-        internal readonly bool explicitly;
         internal readonly Asn1Encodable obj;
 
-        /**
+		/**
+         * @param explicitly true if the object is explicitly tagged.
          * @param tagNo the tag number for this object.
          * @param obj the tagged object.
          */
-        protected Asn1TaggedObject(int tagNo, Asn1Encodable obj)
-            : this(true, tagNo, obj)
+        protected Asn1TaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj)
+            : this(isExplicit, Asn1Tags.ContextSpecific, tagNo, obj)
         {
         }
 
-		/**
-         * @param explicitly true if the object is explicitly tagged.
-         * @param tagNo the tag number for this object.
-         * @param obj the tagged object.
-         */
-        protected Asn1TaggedObject(bool explicitly, int tagNo, Asn1Encodable obj)
+        protected Asn1TaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj)
+            : this(isExplicit ? DeclaredExplicit : DeclaredImplicit, tagClass, tagNo, obj)
+        {
+        }
+
+        internal Asn1TaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj)
         {
             if (null == obj)
                 throw new ArgumentNullException("obj");
+            if (Asn1Tags.Universal == tagClass || (tagClass & Asn1Tags.Private) != tagClass)
+                throw new ArgumentException("invalid tag class: " + tagClass, "tagClass");
 
-            // IAsn1Choice marker interface 'insists' on explicit tagging
-            this.explicitly = explicitly || (obj is IAsn1Choice);
+            this.explicitness = (obj is IAsn1Choice) ? DeclaredExplicit : explicitness;
+            this.tagClass = tagClass;
             this.tagNo = tagNo;
             this.obj = obj;
         }
 
-		protected override bool Asn1Equals(Asn1Object asn1Object)
+        protected override bool Asn1Equals(Asn1Object asn1Object)
         {
             if (asn1Object is DerApplicationSpecific)
                 return asn1Object.CallAsn1Equals(this);
 
             Asn1TaggedObject that = asn1Object as Asn1TaggedObject;
-            return null != that
-                && this.tagClass == that.tagClass
-                && this.tagNo == that.tagNo
-                && this.explicitly == that.explicitly   // TODO Should this be part of equality?
-                && this.GetObject().Equals(that.GetObject());
-		}
+            if (null == that || this.tagNo != that.tagNo || this.tagClass != that.tagClass)
+                return false;
 
-		protected override int Asn1GetHashCode()
-		{
-            int code = (tagClass * 7919) ^ tagNo;
+            if (this.explicitness != that.explicitness)
+            {
+                /*
+                 * TODO This seems incorrect for some cases of implicit tags e.g. if one is a
+                 * declared-implicit SET and the other a parsed object.
+                 */
+                if (this.IsExplicit() != that.IsExplicit())
+                    return false;
+            }
 
-			// TODO: actually this is wrong - the problem is that a re-encoded
-			// object may end up with a different hashCode due to implicit
-			// tagging. As implicit tagging is ambiguous if a sequence is involved
-			// it seems the only correct method for both equals and hashCode is to
-			// compare the encodings...
-//			code ^= explicitly.GetHashCode();
+            Asn1Object p1 = this.obj.ToAsn1Object();
+            Asn1Object p2 = that.obj.ToAsn1Object();
 
-            code ^= obj.GetHashCode();
+            if (p1 == p2)
+                return true;
+
+            if (!this.IsExplicit())
+            {
+                try
+                {
+                    byte[] d1 = this.GetEncoded();
+                    byte[] d2 = that.GetEncoded();
 
-			return code;
+                    return Arrays.AreEqual(d1, d2);
+                }
+                catch (IOException)
+                {
+                    return false;
+                }
+            }
+
+            return p1.CallAsn1Equals(p2);
+		}
+
+		protected override int Asn1GetHashCode()
+		{
+            return (tagClass * 7919) ^ tagNo ^ (IsExplicit() ? 0x0F : 0xF0) ^ obj.ToAsn1Object().CallAsn1GetHashCode();
         }
 
         public int TagClass
@@ -142,6 +159,12 @@ namespace Org.BouncyCastle.Asn1
             return this.tagClass == tagClass && this.tagNo == tagNo;
         }
 
+        [Obsolete("Will be removed. Replace with constant return value of 'false'")]
+        public bool IsEmpty()
+        {
+            return false;
+        }
+
         /**
          * return whether or not the object may be explicitly tagged.
          * <p>
@@ -153,13 +176,27 @@ namespace Org.BouncyCastle.Asn1
          */
         public bool IsExplicit()
         {
-            return explicitly;
+            // TODO New methods like 'IsKnownExplicit' etc. to distinguish uncertain cases?
+            switch (explicitness)
+            {
+            case DeclaredExplicit:
+            case ParsedExplicit:
+                return true;
+            default:
+                return false;
+            }
         }
 
-        [Obsolete("Will be removed. Replace with constant return value of 'false'")]
-        public bool IsEmpty()
+        internal bool IsParsed()
         {
-            return false;
+            switch (explicitness)
+            {
+            case ParsedExplicit:
+            case ParsedImplicit:
+                return true;
+            default:
+                return false;
+            }
         }
 
         /**
@@ -198,6 +235,18 @@ namespace Org.BouncyCastle.Asn1
         }
 
         /**
+         * Return true if the object is marked as constructed, false otherwise.
+         *
+         * @return true if constructed, otherwise false.
+         */
+        // TODO Need this public if/when DerApplicationSpecific extends Asn1TaggedObject
+        internal bool IsConstructed()
+        {
+            int encoding = Asn1Encoding == Ber ? Asn1OutputStream.EncodingBer : Asn1OutputStream.EncodingDer;
+            return EncodeConstructed(encoding);
+        }
+
+        /**
          * return whatever was following the tag.
          * <p>
          * Note: tagged objects are generally context dependent if you're
@@ -245,6 +294,34 @@ namespace Org.BouncyCastle.Asn1
             return CheckedCast(obj.ToAsn1Object());
         }
 
+        public Asn1TaggedObject GetImplicitBaseTagged(int baseTagClass, int baseTagNo)
+        {
+            if (Asn1Tags.Universal == baseTagClass || (baseTagClass & Asn1Tags.Private) != baseTagClass)
+                throw new ArgumentException("invalid base tag class: " + baseTagClass, "baseTagClass");
+
+            switch (explicitness)
+            {
+            case DeclaredExplicit:
+                throw new InvalidOperationException("object explicit - implicit expected.");
+
+            case DeclaredImplicit:
+            {
+                Asn1TaggedObject declared = CheckedCast(obj.ToAsn1Object());
+                if (!declared.HasTag(baseTagClass, baseTagNo))
+                {
+                    string expected = Asn1Utilities.GetTagText(baseTagClass, baseTagNo);
+                    string found = Asn1Utilities.GetTagText(declared);
+                    throw new InvalidOperationException("Expected " + expected + " tag but found " + found);
+                }
+                return declared;
+            }
+
+            // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding)
+            default:
+                return ReplaceTag(baseTagClass, baseTagNo);
+            }
+        }
+
         /**
 		* Return the object held in this tagged object as a parser assuming it has
 		* the type of the passed in tag. If the object doesn't have a parser
@@ -267,10 +344,8 @@ namespace Org.BouncyCastle.Asn1
 				return Asn1Set.GetInstance(this, isExplicit).Parser;
 			}
 
-			if (isExplicit)
-			{
-				return GetObject();
-			}
+            if (isExplicit)
+                return obj.ToAsn1Object();
 
 			throw Platform.CreateNotImplementedException("implicit tagging for tag: " + tag);
 		}
@@ -284,6 +359,8 @@ namespace Org.BouncyCastle.Asn1
 
         internal abstract Asn1Sequence RebuildConstructed(Asn1Object asn1Object);
 
+        internal abstract Asn1TaggedObject ReplaceTag(int tagClass, int tagNo);
+
         internal static Asn1Object CreateConstructed(int tagClass, int tagNo, bool isIL,
             Asn1EncodableVector contentsElements)
         {
@@ -291,29 +368,47 @@ namespace Org.BouncyCastle.Asn1
 
             if (isIL)
             {
-                if (Asn1Tags.Application == tagClass)
-                    return new BerApplicationSpecific(tagNo, contentsElements);
+                Asn1TaggedObject taggedObject = maybeExplicit
+                    ?   new BerTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0])
+                    :   new BerTaggedObject(ParsedImplicit, tagClass, tagNo, BerSequence.FromVector(contentsElements));
 
-                return maybeExplicit
-                    ? new BerTaggedObject(true, tagNo, contentsElements[0])
-                    : new BerTaggedObject(false, tagNo, BerSequence.FromVector(contentsElements));
+                switch (tagClass)
+                {
+                case Asn1Tags.Application:
+                    return new BerApplicationSpecific(taggedObject);
+                default:
+                    return taggedObject;
+                }
             }
+            else
+            {
+                Asn1TaggedObject taggedObject = maybeExplicit
+                    ?   new DLTaggedObject(ParsedExplicit, tagClass, tagNo, contentsElements[0])
+                    :   new DLTaggedObject(ParsedImplicit, tagClass, tagNo, DLSequence.FromVector(contentsElements));
 
-            if (Asn1Tags.Application == tagClass)
-                return new DerApplicationSpecific(tagNo, contentsElements);
-
-            return maybeExplicit
-                ? new DLTaggedObject(true, tagNo, contentsElements[0])
-                : new DLTaggedObject(false, tagNo, DLSequence.FromVector(contentsElements));
+                switch (tagClass)
+                {
+                case Asn1Tags.Application:
+                    return new DLApplicationSpecific(taggedObject);
+                default:
+                    return taggedObject;
+                }
+            }
         }
 
         internal static Asn1Object CreatePrimitive(int tagClass, int tagNo, byte[] contentsOctets)
         {
             // Note: !CONSTRUCTED => IMPLICIT
-            if (Asn1Tags.Application == tagClass)
-                return new DerApplicationSpecific(false, tagNo, contentsOctets);
+            Asn1TaggedObject taggedObject = new DLTaggedObject(ParsedImplicit, tagClass, tagNo,
+                new DerOctetString(contentsOctets));
 
-            return new DLTaggedObject(false, tagNo, new DerOctetString(contentsOctets));
+            switch (tagClass)
+            {
+            case Asn1Tags.Application:
+                return new DLApplicationSpecific(taggedObject);
+            default:
+                return taggedObject;
+            }
         }
 
         private static Asn1TaggedObject CheckedCast(Asn1Object asn1Object)
diff --git a/crypto/src/asn1/BerApplicationSpecific.cs b/crypto/src/asn1/BerApplicationSpecific.cs
index 65fbecbe1..2a874984f 100644
--- a/crypto/src/asn1/BerApplicationSpecific.cs
+++ b/crypto/src/asn1/BerApplicationSpecific.cs
@@ -5,11 +5,43 @@ namespace Org.BouncyCastle.Asn1
 	public class BerApplicationSpecific
 		: DerApplicationSpecific
 	{
-		public BerApplicationSpecific(
-			int					tagNo,
-			Asn1EncodableVector	vec)
-			: base(tagNo, vec)
-		{
-		}
+        /**
+         * Create an application specific object with an explicit tag
+         *
+         * @param tagNo the tag number for this object.
+         * @param baseEncodable the object to be contained.
+         */
+        public BerApplicationSpecific(int tagNo, Asn1Encodable baseEncodable)
+            : this(true, tagNo, baseEncodable)
+        {
+        }
+
+        /**
+         * Create an application specific object with the tagging style given by the value of explicit.
+         *
+         * @param explicit true if the object is explicitly tagged.
+         * @param tagNo the tag number for this object.
+         * @param baseEncodable the object to be contained.
+         */
+        public BerApplicationSpecific(bool isExplicit, int tagNo, Asn1Encodable baseEncodable)
+            : base(new BerTaggedObject(isExplicit, Asn1Tags.Application, tagNo, baseEncodable))
+        {
+        }
+
+        /**
+         * Create an application specific object which is marked as constructed
+         *
+         * @param tagNo the tag number for this object.
+         * @param contentsElements the objects making up the application specific object.
+         */
+        public BerApplicationSpecific(int tagNo, Asn1EncodableVector contentsElements)
+            : base(new BerTaggedObject(false, Asn1Tags.Application, tagNo, BerSequence.FromVector(contentsElements)))
+        {
+        }
+
+        internal BerApplicationSpecific(Asn1TaggedObject taggedObject)
+            : base(taggedObject)
+        {
+        }
 	}
 }
diff --git a/crypto/src/asn1/BerTaggedObject.cs b/crypto/src/asn1/BerTaggedObject.cs
index fc4d1835a..a97a8e143 100644
--- a/crypto/src/asn1/BerTaggedObject.cs
+++ b/crypto/src/asn1/BerTaggedObject.cs
@@ -10,39 +10,49 @@ namespace Org.BouncyCastle.Asn1
 	public class BerTaggedObject
 		: DerTaggedObject
 	{
-		/**
+        /**
+		 * create an implicitly tagged object that contains a zero
+		 * length sequence.
+		 */
+        [Obsolete("Will be removed")]
+        public BerTaggedObject(int tagNo)
+            : base(false, tagNo, BerSequence.Empty)
+        {
+        }
+
+        /**
 		 * @param tagNo the tag number for this object.
 		 * @param obj the tagged object.
 		 */
-		public BerTaggedObject(
-			int				tagNo,
-			Asn1Encodable	obj)
-			: base(tagNo, obj)
+        public BerTaggedObject(int tagNo, Asn1Encodable obj)
+			: base(true, tagNo, obj)
 		{
 		}
 
-		/**
-		 * @param explicitly true if an explicitly tagged object.
+        public BerTaggedObject(int tagClass, int tagNo, Asn1Encodable obj)
+            : base(true, tagClass, tagNo, obj)
+        {
+        }
+
+        /**
+		 * @param isExplicit true if an explicitly tagged object.
 		 * @param tagNo the tag number for this object.
 		 * @param obj the tagged object.
 		 */
-		public BerTaggedObject(
-			bool			explicitly,
-			int				tagNo,
-			Asn1Encodable	obj)
-			: base(explicitly, tagNo, obj)
+        public BerTaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj)
+			: base(isExplicit, tagNo, obj)
 		{
 		}
 
-		/**
-		 * create an implicitly tagged object that contains a zero
-		 * length sequence.
-		 */
-		public BerTaggedObject(
-			int tagNo)
-			: base(false, tagNo, BerSequence.Empty)
-		{
-		}
+        public BerTaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj)
+            : base(isExplicit, tagClass, tagNo, obj)
+        {
+        }
+
+        internal BerTaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj)
+            : base(explicitness, tagClass, tagNo, obj)
+        {
+        }
 
         internal override string Asn1Encoding
         {
@@ -116,5 +126,10 @@ namespace Org.BouncyCastle.Asn1
         {
             return new BerSequence(asn1Object);
         }
+
+        internal override Asn1TaggedObject ReplaceTag(int tagClass, int tagNo)
+        {
+            return new BerTaggedObject(explicitness, tagClass, tagNo, obj);
+        }
     }
 }
diff --git a/crypto/src/asn1/DLApplicationSpecific.cs b/crypto/src/asn1/DLApplicationSpecific.cs
new file mode 100644
index 000000000..8fffcf65b
--- /dev/null
+++ b/crypto/src/asn1/DLApplicationSpecific.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal class DLApplicationSpecific
+        : DerApplicationSpecific
+    {
+        internal DLApplicationSpecific(int tagNo, Asn1Encodable baseEncodable)
+            : this(true, tagNo, baseEncodable)
+        {
+        }
+
+        internal DLApplicationSpecific(bool isExplicit, int tagNo, Asn1Encodable baseEncodable)
+            : base(new DLTaggedObject(isExplicit, Asn1Tags.Application, tagNo, baseEncodable))
+        {
+        }
+
+        internal DLApplicationSpecific(int tagNo, Asn1EncodableVector contentsElements)
+            : base(new DLTaggedObject(false, Asn1Tags.Application, tagNo, DLSequence.FromVector(contentsElements)))
+        {
+        }
+
+        internal DLApplicationSpecific(Asn1TaggedObject taggedObject)
+            : base(taggedObject)
+        {
+        }
+    }
+}
diff --git a/crypto/src/asn1/DLTaggedObject.cs b/crypto/src/asn1/DLTaggedObject.cs
index 1c5e990b4..314b42799 100644
--- a/crypto/src/asn1/DLTaggedObject.cs
+++ b/crypto/src/asn1/DLTaggedObject.cs
@@ -12,13 +12,23 @@ namespace Org.BouncyCastle.Asn1
         {
         }
 
-        internal DLTaggedObject(bool explicitly, int tagNo, Asn1Encodable obj)
-            : base(explicitly, tagNo, obj)
+        internal DLTaggedObject(int tagClass, int tagNo, Asn1Encodable obj)
+            : base(tagClass, tagNo, obj)
         {
         }
 
-        internal DLTaggedObject(int tagNo)
-            : base(false, tagNo, DLSequence.Empty)
+        internal DLTaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj)
+            : base(isExplicit, tagNo, obj)
+        {
+        }
+
+        internal DLTaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj)
+            : base(isExplicit, tagClass, tagNo, obj)
+        {
+        }
+
+        internal DLTaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj)
+            : base(explicitness, tagClass, tagNo, obj)
         {
         }
 
@@ -97,6 +107,11 @@ namespace Org.BouncyCastle.Asn1
             return new DLSequence(asn1Object);
         }
 
+        internal override Asn1TaggedObject ReplaceTag(int tagClass, int tagNo)
+        {
+            return new DLTaggedObject(explicitness, tagClass, tagNo, obj);
+        }
+
         private int GetContentsLengthDL(Asn1Object baseObject, bool withBaseID)
         {
             if (m_contentsLengthDL < 0)
diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs
index bc48d24e5..39c2aec28 100644
--- a/crypto/src/asn1/DerApplicationSpecific.cs
+++ b/crypto/src/asn1/DerApplicationSpecific.cs
@@ -11,225 +11,219 @@ namespace Org.BouncyCastle.Asn1
     public class DerApplicationSpecific
         : Asn1Object
     {
-		private readonly bool	isConstructed;
-        private readonly int	tag;
-        private readonly byte[]	octets;
-
-		internal DerApplicationSpecific(
-			bool	isConstructed,
-			int		tag,
-			byte[]	octets)
-		{
-			this.isConstructed = isConstructed;
-			this.tag = tag;
-			this.octets = octets;
-		}
-
-		public DerApplicationSpecific(
-            int		tag,
-            byte[]	octets)
-			: this(false, tag, octets)
-        {
-        }
-
-		public DerApplicationSpecific(
-			int				tag, 
-			Asn1Encodable	obj) 
-			: this(true, tag, obj)
-		{
-		}
-
-		public DerApplicationSpecific(
-			bool			isExplicit,
-			int				tag,
-			Asn1Encodable	obj)
-		{
-            Asn1Object asn1Obj = obj.ToAsn1Object();
-
-            byte[] data = asn1Obj.GetDerEncoded();
-
-            this.isConstructed = Asn1TaggedObject.IsConstructed(isExplicit, asn1Obj);
-			this.tag = tag;
-
-			if (isExplicit)
-			{
-				this.octets = data;
-			}
-			else
-			{
-				int lenBytes = GetLengthOfHeader(data);
-				byte[] tmp = new byte[data.Length - lenBytes];
-				Array.Copy(data, lenBytes, tmp, 0, tmp.Length);
-				this.octets = tmp;
-			}
-		}
-
-		public DerApplicationSpecific(
-			int					tagNo,
-			Asn1EncodableVector	vec)
-		{
-			this.tag = tagNo;
-			this.isConstructed = true;
-			MemoryStream bOut = new MemoryStream();
-
-			for (int i = 0; i != vec.Count; i++)
-			{
-				try
-				{
-					byte[] bs = vec[i].GetDerEncoded();
-					bOut.Write(bs, 0, bs.Length);
-                }
-				catch (IOException e)
-				{
-					throw new InvalidOperationException("malformed object", e);
-				}
-			}
-			this.octets = bOut.ToArray();
-		}
-
-		private int GetLengthOfHeader(
-			byte[] data)
-		{
-            int length = data[1]; // TODO: assumes 1 byte tag
-
-            if (length == 0x80)
+        public static DerApplicationSpecific GetInstance(object obj)
+        {
+            if (obj == null || obj is DerApplicationSpecific)
             {
-                return 2;      // indefinite-length encoding
+                return (DerApplicationSpecific)obj;
             }
-
-            if (length > 127)
+            else if (obj is byte[])
             {
-                int size = length & 0x7f;
-
-                // Note: The invalid long form "0xff" (see X.690 8.1.3.5c) will be caught here
-                if (size > 4)
+                try
                 {
-                    throw new InvalidOperationException("DER length more than 4 bytes: " + size);
+                    return GetInstance(FromByteArray((byte[])obj));
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct application-specific from byte[]: " + e.Message);
                 }
-
-                return size + 2;
             }
 
-            return 2;
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
         }
 
-		public bool IsConstructed()
+        internal readonly Asn1TaggedObject m_taggedObject;
+
+        /**
+         * Create an application specific object from the passed in data. This will assume
+         * the data does not represent a constructed object.
+         *
+         * @param tagNo the tag number for this object.
+         * @param contentsOctets the encoding of the object's body.
+         */
+        public DerApplicationSpecific(int tagNo, byte[] contentsOctets)
+            : this(new DerTaggedObject(false, Asn1Tags.Application, tagNo, new DerOctetString(contentsOctets)))
         {
-			return isConstructed;
         }
 
-		public byte[] GetContents()
+        /**
+         * Create an application specific object with a tagging of explicit/constructed.
+         *
+         * @param tag the tag number for this object.
+         * @param object the object to be contained.
+         */
+        public DerApplicationSpecific(int tag, Asn1Encodable baseEncodable)
+            : this(true, tag, baseEncodable)
         {
-            return octets;
         }
 
-		public int ApplicationTag
+        /**
+         * Create an application specific object with the tagging style given by the value of explicit.
+         *
+         * @param explicit true if the object is explicitly tagged.
+         * @param tagNo the tag number for this object.
+         * @param baseEncodable the object to be contained.
+         */
+        public DerApplicationSpecific(bool isExplicit, int tagNo, Asn1Encodable baseEncodable)
+            : this(new DerTaggedObject(isExplicit, Asn1Tags.Application, tagNo, baseEncodable))
         {
-            get { return tag; }
         }
 
-		/**
-		 * Return the enclosed object assuming explicit tagging.
-		 *
-		 * @return  the resulting object
-		 * @throws IOException if reconstruction fails.
-		 */
-		public Asn1Object GetObject()
+        /**
+         * Create an application specific object which is marked as constructed
+         *
+         * @param tagNo the tag number for this object.
+         * @param contentsElements   the objects making up the application specific object.
+         */
+        public DerApplicationSpecific(int tagNo, Asn1EncodableVector contentsElements)
+            : this(new DerTaggedObject(false, Asn1Tags.Application, tagNo, DerSequence.FromVector(contentsElements)))
         {
-			return FromByteArray(GetContents());
-		}
+        }
 
-		/**
-		 * Return the enclosed object assuming implicit tagging.
-		 *
-		 * @param derTagNo the type tag that should be applied to the object's contents.
-		 * @return  the resulting object
-		 * @throws IOException if reconstruction fails.
-		 */
-		public Asn1Object GetObject(
-			int derTagNo)
-		{
-			if (derTagNo >= 0x1f)
-				throw new IOException("unsupported tag number");
+        internal DerApplicationSpecific(Asn1TaggedObject taggedObject)
+            //: base(taggedObject.explicitness, CheckTagClass(taggedObject.tagClass), taggedObject.tagNo,
+            //      taggedObject.obj)
+        {
+            CheckTagClass(taggedObject.TagClass);
 
-			byte[] orig = this.GetEncoded();
-			byte[] tmp = ReplaceTagNumber(derTagNo, orig);
+            this.m_taggedObject = taggedObject;
+        }
 
-			if ((orig[0] & Asn1Tags.Constructed) != 0)
-			{
-				tmp[0] |= Asn1Tags.Constructed;
-			}
+        public int ApplicationTag
+        {
+            get { return m_taggedObject.TagNo; }
+        }
 
-			return FromByteArray(tmp);
-		}
+        public byte[] GetContents()
+        {
+            return m_taggedObject.GetContents();
+        }
 
-        internal override bool EncodeConstructed(int encoding)
+        public Asn1Object GetEnclosedObject()
         {
-            return isConstructed;
+            return m_taggedObject.GetBaseObject().ToAsn1Object();
         }
 
-        internal override int EncodedLength(int encoding, bool withID)
+        [Obsolete("Use GetEnclosedObject instead")]
+        public Asn1Object GetObject()
         {
-            return Asn1OutputStream.GetLengthOfEncodingDL(withID, tag, octets.Length);
+            return GetEnclosedObject();
         }
 
-        internal override void Encode(Asn1OutputStream asn1Out, bool withID)
+        public Asn1Object GetObject(int tagNo)
+        {
+            // TODO[asn1] Implement Asn1TaggedObject.GetBaseUniversal
+            //return taggedObject.GetBaseUniversal(false, tagNo);
+
+            if (tagNo >= 0x1F)
+                throw new IOException("unsupported tag number");
+
+            byte[] orig = this.GetEncoded();
+            byte[] tmp = ReplaceTagNumber(tagNo, orig);
+
+            if ((orig[0] & Asn1Tags.Constructed) != 0)
+            {
+                tmp[0] |= Asn1Tags.Constructed;
+            }
+
+            return FromByteArray(tmp);
+        }
+
+        public bool HasApplicationTag(int tagNo)
+        {
+            return m_taggedObject.HasTag(Asn1Tags.Application, tagNo);
+        }
+
+        public bool IsConstructed()
+        {
+            return m_taggedObject.IsConstructed();
+        }
+
+        /**
+         * DerApplicationSpecific uses an internal Asn1TaggedObject for the
+         * implementation, and will soon be deprecated in favour of using
+         * Asn1TaggedObject with a tag class of {@link Asn1Tags#Application}. This method
+         * lets you get the internal Asn1TaggedObject so that client code can begin the
+         * migration.
+         */
+        public Asn1TaggedObject TaggedObject
         {
-			int flags = Asn1Tags.Application;
-			if (isConstructed)
-			{
-                flags |= Asn1Tags.Constructed; 
-			}
+            get { return m_taggedObject; }
+        }
 
-            asn1Out.WriteEncodingDL(withID, flags, tag, octets);
-		}
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            Asn1TaggedObject that;
+            if (asn1Object is DerApplicationSpecific)
+            {
+                that = ((DerApplicationSpecific)asn1Object).m_taggedObject;
+            }
+            else if (asn1Object is Asn1TaggedObject)
+            {
+                that = (Asn1TaggedObject)asn1Object;
+            }
+            else
+            {
+                return false;
+            }
+
+            return m_taggedObject.Equals(that);
+        }
 
-		protected override bool Asn1Equals(
-			Asn1Object asn1Object)
+        protected override int Asn1GetHashCode()
         {
-			DerApplicationSpecific other = asn1Object as DerApplicationSpecific;
+            return m_taggedObject.CallAsn1GetHashCode();
+        }
 
-			if (other == null)
-				return false;
+        internal override bool EncodeConstructed(int encoding)
+        {
+            return m_taggedObject.EncodeConstructed(encoding);
+        }
 
-			return this.isConstructed == other.isConstructed
-				&& this.tag == other.tag
-				&& Arrays.AreEqual(this.octets, other.octets);
+        internal override int EncodedLength(int encoding, bool withID)
+        {
+            return m_taggedObject.EncodedLength(encoding, withID);
         }
 
-		protected override int Asn1GetHashCode()
-		{
-			return isConstructed.GetHashCode() ^ tag.GetHashCode() ^ Arrays.GetHashCode(octets);
+        internal override void Encode(Asn1OutputStream asn1Out, bool withID)
+        {
+            m_taggedObject.Encode(asn1Out, withID);
         }
 
-		private byte[] ReplaceTagNumber(
-			int		newTag,
-			byte[]	input)
-		{
-			int tagNo = input[0] & 0x1f;
-			int index = 1;
+        private byte[] ReplaceTagNumber(int newTag, byte[] input)
+        {
+            int tagNo = input[0] & 0x1f;
+            int index = 1;
 
             // with tagged object tag number is bottom 5 bits, or stored at the start of the content
-			if (tagNo == 0x1f)
-			{
-				int b = input[index++];
+            if (tagNo == 0x1f)
+            {
+                int b = input[index++];
 
                 // X.690-0207 8.1.2.4.2
-				// "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
-				if ((b & 0x7f) == 0) // Note: -1 will pass
-					throw new IOException("corrupted stream - invalid high tag number found");
+                // "c) bits 7 to 1 of the first subsequent octet shall not all be zero."
+                if ((b & 0x7f) == 0) // Note: -1 will pass
+                    throw new IOException("corrupted stream - invalid high tag number found");
 
                 while ((b & 0x80) != 0)
-				{
-					b = input[index++];
-				}
-			}
+                {
+                    b = input[index++];
+                }
+            }
 
             int remaining = input.Length - index;
             byte[] tmp = new byte[1 + remaining];
             tmp[0] = (byte)newTag;
-			Array.Copy(input, index, tmp, 1, remaining);
-			return tmp;
-		}
+            Array.Copy(input, index, tmp, 1, remaining);
+            return tmp;
+        }
+
+        private static int CheckTagClass(int tagClass)
+        {
+            if (Asn1Tags.Application != tagClass)
+                throw new ArgumentException();
+
+            return tagClass;
+        }
     }
 }
diff --git a/crypto/src/asn1/DerTaggedObject.cs b/crypto/src/asn1/DerTaggedObject.cs
index e58590c8d..66f804ebb 100644
--- a/crypto/src/asn1/DerTaggedObject.cs
+++ b/crypto/src/asn1/DerTaggedObject.cs
@@ -13,38 +13,44 @@ namespace Org.BouncyCastle.Asn1
         private int m_contentsLengthDer = -1;
 
         /**
-		 * @param tagNo the tag number for this object.
-		 * @param obj the tagged object.
+		 * create an implicitly tagged object that contains a zero
+		 * length sequence.
 		 */
-        public DerTaggedObject(
-			int				tagNo,
-			Asn1Encodable	obj)
-			: base(tagNo, obj)
+        [Obsolete("Will be removed")]
+        public DerTaggedObject(int tagNo)
+            : base(false, tagNo, DerSequence.Empty)
+        {
+        }
+
+        public DerTaggedObject(int tagNo, Asn1Encodable obj)
+			: base(true, tagNo, obj)
 		{
 		}
 
-		/**
-		 * @param explicitly true if an explicitly tagged object.
+        public DerTaggedObject(int tagClass, int tagNo, Asn1Encodable obj)
+            : base(true, tagClass, tagNo, obj)
+        {
+        }
+
+        /**
+		 * @param isExplicit true if an explicitly tagged object.
 		 * @param tagNo the tag number for this object.
 		 * @param obj the tagged object.
 		 */
-		public DerTaggedObject(
-			bool			explicitly,
-			int				tagNo,
-			Asn1Encodable	obj)
-			: base(explicitly, tagNo, obj)
+        public DerTaggedObject(bool isExplicit, int tagNo, Asn1Encodable obj)
+			: base(isExplicit, tagNo, obj)
 		{
 		}
 
-		/**
-		 * create an implicitly tagged object that contains a zero
-		 * length sequence.
-		 */
-		public DerTaggedObject(
-			int tagNo)
-			: base(false, tagNo, DerSequence.Empty)
-		{
-		}
+        public DerTaggedObject(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj)
+            : base(isExplicit, tagClass, tagNo, obj)
+        {
+        }
+
+        internal DerTaggedObject(int explicitness, int tagClass, int tagNo, Asn1Encodable obj)
+            : base(explicitness, tagClass, tagNo, obj)
+        {
+        }
 
         internal override string Asn1Encoding
         {
@@ -108,6 +114,11 @@ namespace Org.BouncyCastle.Asn1
             return new DerSequence(asn1Object);
         }
 
+        internal override Asn1TaggedObject ReplaceTag(int tagClass, int tagNo)
+        {
+            return new DerTaggedObject(explicitness, tagClass, tagNo, obj);
+        }
+
         private int GetContentsLengthDer(Asn1Object baseObject, bool withBaseID)
         {
             if (m_contentsLengthDer < 0)
diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs
index 0d3382d00..f573d3663 100644
--- a/crypto/src/asn1/util/Asn1Dump.cs
+++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -23,11 +23,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
          *
          * @param obj the Asn1Object to be dumped out.
          */
-        private static void AsString(
-            string			indent,
-            bool			verbose,
-            Asn1Object		obj,
-            StringBuilder	buf)
+        private static void AsString(string indent, bool verbose, Asn1Object obj, StringBuilder buf)
         {
             if (obj is Asn1Null)
             {
@@ -42,7 +38,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
                 {
                     buf.Append("BER Sequence");
                 }
-                else if (obj is DerSequence)
+                else if (!(obj is DLSequence))
                 {
                     buf.Append("DER Sequence");
                 }
@@ -67,7 +63,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
                 {
                     buf.Append("BER Set");
                 }
-                else if (obj is DerSet)
+                else if (!(obj is DLSet))
                 {
                     buf.Append("DER Set");
                 }
@@ -85,23 +81,29 @@ namespace Org.BouncyCastle.Asn1.Utilities
                     AsString(elementsIndent, verbose, set[i].ToAsn1Object(), buf);
                 }
             }
+            else if (obj is DerApplicationSpecific)
+            {
+                AsString(indent, verbose, ((DerApplicationSpecific)obj).TaggedObject, buf);
+            }
             else if (obj is Asn1TaggedObject)
             {
-                string tab = indent + Tab;
                 buf.Append(indent);
                 if (obj is BerTaggedObject)
                 {
-                    buf.Append("BER Tagged [");
+                    buf.Append("BER Tagged ");
+                }
+                else if (!(obj is DLTaggedObject))
+                {
+                    buf.Append("DER Tagged ");
                 }
                 else
                 {
-                    buf.Append("Tagged [");
+                    buf.Append("Tagged ");
                 }
 
                 Asn1TaggedObject o = (Asn1TaggedObject)obj;
 
-                buf.Append(o.TagNo.ToString());
-                buf.Append(']');
+                buf.Append(Asn1Utilities.GetTagText(o));
 
                 if (!o.IsExplicit())
                 {
@@ -110,7 +112,9 @@ namespace Org.BouncyCastle.Asn1.Utilities
 
                 buf.Append(NewLine);
 
-                AsString(tab, verbose, o.GetObject(), buf);
+                string baseIndent = indent + Tab;
+
+                AsString(baseIndent, verbose, o.GetBaseObject().ToAsn1Object(), buf);
             }
             else if (obj is DerObjectIdentifier)
             {
@@ -131,20 +135,20 @@ namespace Org.BouncyCastle.Asn1.Utilities
             else if (obj is BerOctetString)
             {
                 byte[] octets = ((Asn1OctetString)obj).GetOctets();
-                string extra = verbose ? dumpBinaryDataAsString(indent, octets) : "";
+                string extra = verbose ? DumpBinaryDataAsString(indent, octets) : "";
                 buf.Append(indent + "BER Octet String" + "[" + octets.Length + "] " + extra + NewLine);
             }
             else if (obj is DerOctetString)
             {
                 byte[] octets = ((Asn1OctetString)obj).GetOctets();
-                string extra = verbose ? dumpBinaryDataAsString(indent, octets) : "";
+                string extra = verbose ? DumpBinaryDataAsString(indent, octets) : "";
                 buf.Append(indent + "DER Octet String" + "[" + octets.Length + "] " + extra + NewLine);
             }
             else if (obj is DerBitString)
             {
                 DerBitString bt = (DerBitString)obj; 
                 byte[] bytes = bt.GetBytes();
-                string extra = verbose ? dumpBinaryDataAsString(indent, bytes) : "";
+                string extra = verbose ? DumpBinaryDataAsString(indent, bytes) : "";
                 buf.Append(indent + "DER Bit String" + "[" + bytes.Length + ", " + bt.PadBits + "] " + extra + NewLine);
             }
             else if (obj is DerIA5String)
@@ -187,14 +191,6 @@ namespace Org.BouncyCastle.Asn1.Utilities
             {
                 buf.Append(indent + "GeneralizedTime(" + ((DerGeneralizedTime)obj).GetTime() + ") " + NewLine);
             }
-            else if (obj is BerApplicationSpecific)
-            {
-                buf.Append(outputApplicationSpecific("BER", indent, verbose, (BerApplicationSpecific)obj));
-            }
-            else if (obj is DerApplicationSpecific)
-            {
-                buf.Append(outputApplicationSpecific("DER", indent, verbose, (DerApplicationSpecific)obj));
-            }
             else if (obj is DerEnumerated)
             {
                 DerEnumerated en = (DerEnumerated)obj;
@@ -227,44 +223,13 @@ namespace Org.BouncyCastle.Asn1.Utilities
             }
         }
 
-        private static string outputApplicationSpecific(
-            string					type,
-            string					indent,
-            bool					verbose,
-            DerApplicationSpecific	app)
-        {
-            StringBuilder buf = new StringBuilder();
-
-            if (app.IsConstructed())
-            {
-                try
-                {
-                    Asn1Sequence s = Asn1Sequence.GetInstance(app.GetObject(Asn1Tags.Sequence));
-                    buf.Append(indent + type + " ApplicationSpecific[" + app.ApplicationTag + "]" + NewLine);
-                    foreach (Asn1Encodable ae in s)
-                    {
-                        AsString(indent + Tab, verbose, ae.ToAsn1Object(), buf);
-                    }
-                }
-                catch (IOException e)
-                {
-                    buf.Append(e);
-                }
-                return buf.ToString();
-            }
-
-            return indent + type + " ApplicationSpecific[" + app.ApplicationTag + "] ("
-                + Hex.ToHexString(app.GetContents()) + ")" + NewLine;
-        }
-
         /**
          * dump out a DER object as a formatted string, in non-verbose mode
          *
          * @param obj the Asn1Encodable to be dumped out.
          * @return  the resulting string.
          */
-        public static string DumpAsString(
-            Asn1Encodable obj)
+        public static string DumpAsString(Asn1Encodable obj)
         {
             return DumpAsString(obj, false);
         }
@@ -276,16 +241,14 @@ namespace Org.BouncyCastle.Asn1.Utilities
          * @param verbose  if true, dump out the contents of octet and bit strings.
          * @return  the resulting string.
          */
-        public static string DumpAsString(
-            Asn1Encodable	obj,
-            bool			verbose)
+        public static string DumpAsString(Asn1Encodable obj, bool verbose)
         {
             StringBuilder buf = new StringBuilder();
             AsString("", verbose, obj.ToAsn1Object(), buf);
             return buf.ToString();
         }
 
-        private static string dumpBinaryDataAsString(string indent, byte[] bytes)
+        private static string DumpBinaryDataAsString(string indent, byte[] bytes)
         {
             indent += Tab;
 
@@ -298,7 +261,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
                     buf.Append(indent);
                     buf.Append(Hex.ToHexString(bytes, i, SampleSize));
                     buf.Append(Tab);
-                    buf.Append(calculateAscString(bytes, i, SampleSize));
+                    buf.Append(CalculateAscString(bytes, i, SampleSize));
                     buf.Append(NewLine);
                 }
                 else
@@ -310,7 +273,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
                         buf.Append("  ");
                     }
                     buf.Append(Tab);
-                    buf.Append(calculateAscString(bytes, i, bytes.Length - i));
+                    buf.Append(CalculateAscString(bytes, i, bytes.Length - i));
                     buf.Append(NewLine);
                 }
             }
@@ -318,10 +281,7 @@ namespace Org.BouncyCastle.Asn1.Utilities
             return buf.ToString();
         }
 
-        private static string calculateAscString(
-            byte[]	bytes,
-            int		off,
-            int		len)
+        private static string CalculateAscString(byte[] bytes, int off, int len)
         {
             StringBuilder buf = new StringBuilder();