summary refs log tree commit diff
path: root/crypto/src
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2021-11-18 13:46:18 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2021-11-18 13:46:18 +0700
commit4fccb8f490eefe7181558b2c3376ab23c33632ee (patch)
treefe8c1877bb5567cb4823823af77e5c8a38423db8 /crypto/src
parentASN.1: Staged encoding (diff)
downloadBouncyCastle.NET-ed25519-4fccb8f490eefe7181558b2c3376ab23c33632ee.tar.xz
ASN.1: Port of bc-java TYPE instances
- we use Meta.Instance here due to syntax restrictions
- also reworked some ASN.1 string types
Diffstat (limited to 'crypto/src')
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs40
-rw-r--r--crypto/src/asn1/Asn1Null.cs65
-rw-r--r--crypto/src/asn1/Asn1ObjectDescriptor.cs35
-rw-r--r--crypto/src/asn1/Asn1OctetString.cs69
-rw-r--r--crypto/src/asn1/Asn1RelativeOid.cs23
-rw-r--r--crypto/src/asn1/Asn1Sequence.cs45
-rw-r--r--crypto/src/asn1/Asn1Set.cs49
-rw-r--r--crypto/src/asn1/Asn1Tag.cs31
-rw-r--r--crypto/src/asn1/Asn1TaggedObject.cs71
-rw-r--r--crypto/src/asn1/Asn1Type.cs29
-rw-r--r--crypto/src/asn1/Asn1UniversalType.cs56
-rw-r--r--crypto/src/asn1/Asn1UniversalTypes.cs74
-rw-r--r--crypto/src/asn1/Asn1Utilities.cs104
-rw-r--r--crypto/src/asn1/DERExternal.cs25
-rw-r--r--crypto/src/asn1/DerApplicationSpecific.cs45
-rw-r--r--crypto/src/asn1/DerBMPString.cs108
-rw-r--r--crypto/src/asn1/DerBitString.cs21
-rw-r--r--crypto/src/asn1/DerBoolean.cs81
-rw-r--r--crypto/src/asn1/DerEnumerated.cs119
-rw-r--r--crypto/src/asn1/DerGeneralString.cs96
-rw-r--r--crypto/src/asn1/DerGeneralizedTime.cs75
-rw-r--r--crypto/src/asn1/DerGraphicString.cs47
-rw-r--r--crypto/src/asn1/DerIA5String.cs135
-rw-r--r--crypto/src/asn1/DerInteger.cs62
-rw-r--r--crypto/src/asn1/DerNumericString.cs177
-rw-r--r--crypto/src/asn1/DerObjectIdentifier.cs31
-rw-r--r--crypto/src/asn1/DerPrintableString.cs126
-rw-r--r--crypto/src/asn1/DerT61String.cs116
-rw-r--r--crypto/src/asn1/DerUTCTime.cs95
-rw-r--r--crypto/src/asn1/DerUTF8String.cs108
-rw-r--r--crypto/src/asn1/DerUniversalString.cs157
-rw-r--r--crypto/src/asn1/DerVideotexString.cs94
-rw-r--r--crypto/src/asn1/DerVisibleString.cs116
-rw-r--r--crypto/src/asn1/ocsp/CertStatus.cs23
34 files changed, 1622 insertions, 926 deletions
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 3f7a65cc9..d03c7a6b5 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -401,11 +401,12 @@ namespace Org.BouncyCastle.Asn1
             switch (tagNo)
             {
             case Asn1Tags.BmpString:
-                return new DerBmpString(GetBmpCharBuffer(defIn));
+                return DerBmpString.CreatePrimitive(GetBmpCharBuffer(defIn));
             case Asn1Tags.Boolean:
-                return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
+                return DerBoolean.CreatePrimitive(GetBuffer(defIn, tmpBuffers));
             case Asn1Tags.Enumerated:
-                return DerEnumerated.FromOctetString(GetBuffer(defIn, tmpBuffers));
+                // TODO Ideally only clone if we used a buffer
+                return DerEnumerated.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true);
             case Asn1Tags.ObjectIdentifier:
                 // TODO Ideally only clone if we used a buffer
                 return DerObjectIdentifier.CreatePrimitive(GetBuffer(defIn, tmpBuffers), true);
@@ -418,44 +419,39 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.BitString:
                 return DerBitString.CreatePrimitive(bytes);
             case Asn1Tags.GeneralizedTime:
-                return new DerGeneralizedTime(bytes);
+                return DerGeneralizedTime.CreatePrimitive(bytes);
             case Asn1Tags.GeneralString:
-                return new DerGeneralString(bytes);
+                return DerGeneralString.CreatePrimitive(bytes);
             case Asn1Tags.GraphicString:
                 return DerGraphicString.CreatePrimitive(bytes);
             case Asn1Tags.IA5String:
-                return new DerIA5String(bytes);
+                return DerIA5String.CreatePrimitive(bytes);
             case Asn1Tags.Integer:
-                return new DerInteger(bytes, false);
+                return DerInteger.CreatePrimitive(bytes);
             case Asn1Tags.Null:
-            {
-                if (0 != bytes.Length)
-                    throw new InvalidOperationException("malformed NULL encoding encountered");
-
-                return DerNull.Instance;
-            }
+                return Asn1Null.CreatePrimitive(bytes);
             case Asn1Tags.NumericString:
-                return new DerNumericString(bytes);
+                return DerNumericString.CreatePrimitive(bytes);
             case Asn1Tags.ObjectDescriptor:
                 return Asn1ObjectDescriptor.CreatePrimitive(bytes);
             case Asn1Tags.OctetString:
-                return new DerOctetString(bytes);
+                return Asn1OctetString.CreatePrimitive(bytes);
             case Asn1Tags.PrintableString:
-                return new DerPrintableString(bytes);
+                return DerPrintableString.CreatePrimitive(bytes);
             case Asn1Tags.RelativeOid:
                 return Asn1RelativeOid.CreatePrimitive(bytes, false);
             case Asn1Tags.T61String:
-                return new DerT61String(bytes);
+                return DerT61String.CreatePrimitive(bytes);
             case Asn1Tags.UniversalString:
-                return new DerUniversalString(bytes);
+                return DerUniversalString.CreatePrimitive(bytes);
             case Asn1Tags.UtcTime:
-                return new DerUtcTime(bytes);
+                return DerUtcTime.CreatePrimitive(bytes);
             case Asn1Tags.Utf8String:
-                return new DerUtf8String(bytes);
+                return DerUtf8String.CreatePrimitive(bytes);
             case Asn1Tags.VideotexString:
-                return new DerVideotexString(bytes);
+                return DerVideotexString.CreatePrimitive(bytes);
             case Asn1Tags.VisibleString:
-                return new DerVisibleString(bytes);
+                return DerVisibleString.CreatePrimitive(bytes);
             default:
                 throw new IOException("unknown tag " + tagNo + " encountered");
             }
diff --git a/crypto/src/asn1/Asn1Null.cs b/crypto/src/asn1/Asn1Null.cs
index d54019f67..9ea9b4375 100644
--- a/crypto/src/asn1/Asn1Null.cs
+++ b/crypto/src/asn1/Asn1Null.cs
@@ -1,3 +1,8 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Asn1
 {
     /**
@@ -6,13 +11,65 @@ namespace Org.BouncyCastle.Asn1
     public abstract class Asn1Null
         : Asn1Object
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1Null), Asn1Tags.Null) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
+
+        public static Asn1Null GetInstance(object obj)
+        {
+            if (obj == null || obj is Asn1Null)
+            {
+                return (Asn1Null)obj;
+            }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is Asn1Null)
+                    return (Asn1Null)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (Asn1Null)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct NULL from byte[]: " + e.Message);
+                }
+            }
+
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
+        }
+
+        public static Asn1Null GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            return (Asn1Null)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
+
         internal Asn1Null()
         {
         }
 
-		public override string ToString()
-		{
-			return "NULL";
-		}
+        public override string ToString()
+        {
+            return "NULL";
+        }
+
+        internal static Asn1Null CreatePrimitive(byte[] contents)
+        {
+            if (0 != contents.Length)
+                throw new InvalidOperationException("malformed NULL encoding encountered");
+
+            return DerNull.Instance;
+        }
     }
 }
diff --git a/crypto/src/asn1/Asn1ObjectDescriptor.cs b/crypto/src/asn1/Asn1ObjectDescriptor.cs
index 289a0e16f..9c99f441e 100644
--- a/crypto/src/asn1/Asn1ObjectDescriptor.cs
+++ b/crypto/src/asn1/Asn1ObjectDescriptor.cs
@@ -8,6 +8,25 @@ namespace Org.BouncyCastle.Asn1
     public sealed class Asn1ObjectDescriptor
         : Asn1Object
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1ObjectDescriptor), Asn1Tags.ObjectDescriptor) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return new Asn1ObjectDescriptor(
+                    (DerGraphicString)DerGraphicString.Meta.Instance.FromImplicitPrimitive(octetString));
+            }
+
+            internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+            {
+                return new Asn1ObjectDescriptor(
+                    (DerGraphicString)DerGraphicString.Meta.Instance.FromImplicitConstructed(sequence));
+            }
+        }
+
         /**
          * Return an ObjectDescriptor from the passed in object.
          *
@@ -31,7 +50,7 @@ namespace Org.BouncyCastle.Asn1
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (Asn1ObjectDescriptor)Meta.Instance.FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -46,21 +65,13 @@ namespace Org.BouncyCastle.Asn1
          * Return an ObjectDescriptor from a tagged object.
          *
          * @param taggedObject the tagged object holding the object we want.
-         * @param explicit     true if the object is meant to be explicitly tagged,
-         *                     false otherwise.
+         * @param declaredExplicit true if the object is meant to be explicitly tagged, false otherwise.
          * @exception IllegalArgumentException if the tagged object cannot be converted.
          * @return an ASN1ObjectDescriptor instance, or null.
          */
-        public static Asn1ObjectDescriptor GetInstance(Asn1TaggedObject taggedObject, bool isExplicit)
+        public static Asn1ObjectDescriptor GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            if (isExplicit || baseObject is Asn1ObjectDescriptor)
-            {
-                return GetInstance(baseObject);
-            }
-
-            return new Asn1ObjectDescriptor(new DerGraphicString(((Asn1OctetString)baseObject).GetOctets()));
+            return (Asn1ObjectDescriptor)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         private readonly DerGraphicString m_baseGraphicString;
diff --git a/crypto/src/asn1/Asn1OctetString.cs b/crypto/src/asn1/Asn1OctetString.cs
index 66d6fb8d1..d34686134 100644
--- a/crypto/src/asn1/Asn1OctetString.cs
+++ b/crypto/src/asn1/Asn1OctetString.cs
@@ -9,6 +9,23 @@ namespace Org.BouncyCastle.Asn1
     public abstract class Asn1OctetString
         : Asn1Object, Asn1OctetStringParser
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1OctetString), Asn1Tags.OctetString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return octetString;
+            }
+
+            internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+            {
+                return sequence.ToAsn1OctetString();
+            }
+        }
+
         internal static readonly byte[] EmptyOctets = new byte[0];
 
         /**
@@ -28,15 +45,13 @@ namespace Org.BouncyCastle.Asn1
             {
                 Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
                 if (asn1Object is Asn1OctetString)
-                {
                     return (Asn1OctetString)asn1Object;
-                }
             }
             else if (obj is byte[])
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (Asn1OctetString)Meta.Instance.FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -48,46 +63,15 @@ namespace Org.BouncyCastle.Asn1
         }
 
         /**
-         * return an Octet string from a tagged object.
+         * return an octet string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want.
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *              be converted.
+         * @param taggedObject the tagged object holding the object we want.
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
         public static Asn1OctetString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            if (declaredExplicit)
-            {
-                if (!taggedObject.IsExplicit())
-                    throw new ArgumentException("object implicit - explicit expected.");
-
-                return GetInstance(taggedObject.GetObject());
-            }
-
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            // If parsed as explicit though declared implicit, it should have been a set of one
-            if (taggedObject.IsExplicit())
-            {
-                Asn1OctetString singleSegment = GetInstance(baseObject);
-
-                if (taggedObject is BerTaggedObject)
-                    return new BerOctetString(new Asn1OctetString[]{ singleSegment });
-
-                return singleSegment;
-            }
-
-            if (baseObject is Asn1OctetString)
-                return (Asn1OctetString)baseObject;
-
-            // Parser assumes implicit constructed encodings are sequences
-            if (baseObject is Asn1Sequence)
-                return ((Asn1Sequence)baseObject).ToAsn1OctetString();
-
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject),
-                "taggedObject");
+            return (Asn1OctetString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         internal readonly byte[] contents;
@@ -138,5 +122,10 @@ namespace Org.BouncyCastle.Asn1
 		{
 			return "#" + Hex.ToHexString(contents);
 		}
-	}
+
+        internal static Asn1OctetString CreatePrimitive(byte[] contents)
+        {
+            return new DerOctetString(contents);
+        }
+    }
 }
diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs
index a960b50bf..9c4f917f2 100644
--- a/crypto/src/asn1/Asn1RelativeOid.cs
+++ b/crypto/src/asn1/Asn1RelativeOid.cs
@@ -10,6 +10,18 @@ namespace Org.BouncyCastle.Asn1
     public class Asn1RelativeOid
         : Asn1Object
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1RelativeOid), Asn1Tags.RelativeOid) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets(), false);
+            }
+        }
+
         public static Asn1RelativeOid FromContents(byte[] contents)
         {
             return CreatePrimitive(contents, true);
@@ -31,7 +43,7 @@ namespace Org.BouncyCastle.Asn1
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (Asn1RelativeOid)FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -44,14 +56,7 @@ namespace Org.BouncyCastle.Asn1
 
         public static Asn1RelativeOid GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            if (declaredExplicit || baseObject is Asn1RelativeOid)
-            {
-                return GetInstance(baseObject);
-            }
-
-            return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets());
+            return (Asn1RelativeOid)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         private const long LongLimit = (Int64.MaxValue >> 7) - 0x7F;
diff --git a/crypto/src/asn1/Asn1Sequence.cs b/crypto/src/asn1/Asn1Sequence.cs
index cfe0d37aa..d7f84d3e4 100644
--- a/crypto/src/asn1/Asn1Sequence.cs
+++ b/crypto/src/asn1/Asn1Sequence.cs
@@ -10,6 +10,18 @@ namespace Org.BouncyCastle.Asn1
     public abstract class Asn1Sequence
         : Asn1Object, IEnumerable
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1Sequence), Asn1Tags.Sequence) {}
+
+            internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+            {
+                return sequence;
+            }
+        }
+
         /**
          * return an Asn1Sequence from the given object.
          *
@@ -33,7 +45,7 @@ namespace Org.BouncyCastle.Asn1
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (Asn1Sequence)Meta.Instance.FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -55,37 +67,12 @@ namespace Org.BouncyCastle.Asn1
          * be using this method.
          *
          * @param taggedObject the tagged object.
-         * @param declaredExplicit true if the object is meant to be explicitly tagged,
-         *          false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *          be converted.
+         * @param declaredExplicit true if the object is meant to be explicitly tagged, false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
         public static Asn1Sequence GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            if (declaredExplicit)
-            {
-                if (!taggedObject.IsExplicit())
-                    throw new ArgumentException("object implicit - explicit expected.");
-
-                return GetInstance(taggedObject.GetObject());
-            }
-
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            // If parsed as explicit though declared implicit, it should have been a sequence of one
-            if (taggedObject.IsExplicit())
-            {
-                if (taggedObject is BerTaggedObject)
-                    return new BerSequence(baseObject);
-
-                return new DLSequence(baseObject);
-            }
-
-            if (baseObject is Asn1Sequence)
-                return (Asn1Sequence)baseObject;
-
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject),
-                "taggedObject");
+            return (Asn1Sequence)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         // NOTE: Only non-readonly to support LazyDLSequence
diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index 42180fd71..f3b94121b 100644
--- a/crypto/src/asn1/Asn1Set.cs
+++ b/crypto/src/asn1/Asn1Set.cs
@@ -16,6 +16,18 @@ namespace Org.BouncyCastle.Asn1
     public abstract class Asn1Set
         : Asn1Object, IEnumerable
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1Set), Asn1Tags.Set) {}
+
+            internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+            {
+                return sequence.ToAsn1Set();
+            }
+        }
+
         /**
          * return an ASN1Set from the given object.
          *
@@ -39,7 +51,7 @@ namespace Org.BouncyCastle.Asn1
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (Asn1Set)Meta.Instance.FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -61,41 +73,12 @@ namespace Org.BouncyCastle.Asn1
          * be using this method.
          *
          * @param taggedObject the tagged object.
-         * @param declaredExplicit true if the object is meant to be explicitly tagged
-         *          false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *          be converted.
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
         public static Asn1Set GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            if (declaredExplicit)
-            {
-                if (!taggedObject.IsExplicit())
-                    throw new ArgumentException("object implicit - explicit expected.");
-
-                return GetInstance(taggedObject.GetObject());
-            }
-
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            // If parsed as explicit though declared implicit, it should have been a set of one
-            if (taggedObject.IsExplicit())
-            {
-                if (taggedObject is BerTaggedObject)
-                    return new BerSet(baseObject);
-
-                return new DLSet(baseObject);
-            }
-
-            if (baseObject is Asn1Set)
-                return (Asn1Set)baseObject;
-
-            // Parser assumes implicit constructed encodings are sequences
-            if (baseObject is Asn1Sequence)
-                return ((Asn1Sequence)baseObject).ToAsn1Set();
-
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject),
-                "taggedObject");
+            return (Asn1Set)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         // NOTE: Only non-readonly to support LazyDLSet
diff --git a/crypto/src/asn1/Asn1Tag.cs b/crypto/src/asn1/Asn1Tag.cs
new file mode 100644
index 000000000..b57174bdb
--- /dev/null
+++ b/crypto/src/asn1/Asn1Tag.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal sealed class Asn1Tag
+    {
+        internal static Asn1Tag Create(int tagClass, int tagNo)
+        {
+            return new Asn1Tag(tagClass, tagNo);
+        }
+
+        private readonly int m_tagClass;
+        private readonly int m_tagNo;
+
+        private Asn1Tag(int tagClass, int tagNo)
+        {
+            m_tagClass = tagClass;
+            m_tagNo = tagNo;
+        }
+
+        internal int TagClass
+        {
+            get { return m_tagClass; }
+        }
+
+        internal int TagNo
+        {
+            get { return m_tagNo; }
+        }
+    }
+}
diff --git a/crypto/src/asn1/Asn1TaggedObject.cs b/crypto/src/asn1/Asn1TaggedObject.cs
index 7fafa4c52..66835ac14 100644
--- a/crypto/src/asn1/Asn1TaggedObject.cs
+++ b/crypto/src/asn1/Asn1TaggedObject.cs
@@ -238,19 +238,19 @@ namespace Org.BouncyCastle.Asn1
         {
             switch (explicitness)
             {
-                case DeclaredImplicit:
-                {
-                    Asn1Object baseObject = obj.ToAsn1Object();
-                    if (baseObject is Asn1Sequence || baseObject is Asn1Set)
-                        return true;
-
-                    Asn1TaggedObject baseTagged = baseObject as Asn1TaggedObject;
-                    return null != baseTagged && baseTagged.IsConstructed();
-                }
-                case ParsedImplicit:
-                    return obj is Asn1Sequence;
-                default:
+            case DeclaredImplicit:
+            {
+                Asn1Object baseObject = obj.ToAsn1Object();
+                if (baseObject is Asn1Sequence || baseObject is Asn1Set)
                     return true;
+
+                Asn1TaggedObject baseTagged = baseObject as Asn1TaggedObject;
+                return null != baseTagged && baseTagged.IsConstructed();
+            }
+            case ParsedImplicit:
+                return obj is Asn1Sequence;
+            default:
+                return true;
             }
         }
 
@@ -315,13 +315,7 @@ namespace Org.BouncyCastle.Asn1
             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;
+                return Asn1Utilities.CheckTag(declared, baseTagClass, baseTagNo);
             }
 
             // Parsed; return a virtual tag (i.e. that couldn't have been present in the encoding)
@@ -330,6 +324,45 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
+        public Asn1Object GetBaseUniversal(bool declaredExplicit, int tagNo)
+        {
+            Asn1UniversalType universalType = Asn1UniversalTypes.Get(tagNo);
+            if (null == universalType)
+                throw new ArgumentException("unsupported UNIVERSAL tag number: " + tagNo, "tagNo");
+
+            return GetBaseUniversal(declaredExplicit, universalType);
+        }
+
+        internal Asn1Object GetBaseUniversal(bool declaredExplicit, Asn1UniversalType universalType)
+        {
+            if (declaredExplicit)
+            {
+                if (!IsExplicit())
+                    throw new InvalidOperationException("object explicit - implicit expected.");
+
+                return universalType.CheckedCast(obj.ToAsn1Object());
+            }
+
+            if (DeclaredExplicit == explicitness)
+                throw new InvalidOperationException("object explicit - implicit expected.");
+
+            Asn1Object baseObject = obj.ToAsn1Object();
+            switch (explicitness)
+            {
+            case ParsedExplicit:
+                return universalType.FromImplicitConstructed(RebuildConstructed(baseObject));
+            case ParsedImplicit:
+            {
+                if (baseObject is Asn1Sequence)
+                    return universalType.FromImplicitConstructed((Asn1Sequence)baseObject);
+
+                return universalType.FromImplicitPrimitive((DerOctetString)baseObject);
+            }
+            default:
+                return universalType.CheckedCast(baseObject);
+            }
+        }
+
         /**
 		* 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
diff --git a/crypto/src/asn1/Asn1Type.cs b/crypto/src/asn1/Asn1Type.cs
new file mode 100644
index 000000000..f44b74e64
--- /dev/null
+++ b/crypto/src/asn1/Asn1Type.cs
@@ -0,0 +1,29 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal abstract class Asn1Type
+    {
+        internal readonly Type m_platformType;
+
+        internal Asn1Type(Type platformType)
+        {
+            m_platformType = platformType;
+        }
+
+        internal Type PlatformType
+        {
+            get { return m_platformType; }
+        }
+
+        public sealed override bool Equals(object that)
+        {
+            return this == that;
+        }
+
+        public sealed override int GetHashCode()
+        {
+            return base.GetHashCode();
+        }
+    }
+}
diff --git a/crypto/src/asn1/Asn1UniversalType.cs b/crypto/src/asn1/Asn1UniversalType.cs
new file mode 100644
index 000000000..46cacb436
--- /dev/null
+++ b/crypto/src/asn1/Asn1UniversalType.cs
@@ -0,0 +1,56 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal abstract class Asn1UniversalType
+        : Asn1Type
+    {
+        internal readonly Asn1Tag m_tag;
+
+        internal Asn1UniversalType(Type platformType, int tagNo)
+            : base(platformType)
+        {
+            m_tag = Asn1Tag.Create(Asn1Tags.Universal, tagNo);
+        }
+
+        internal Asn1Object CheckedCast(Asn1Object asn1Object)
+        {
+            if (PlatformType.IsInstanceOfType(asn1Object))
+                return asn1Object;
+
+            throw new InvalidOperationException("unexpected object: " + Platform.GetTypeName(asn1Object));
+        }
+
+        internal virtual Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+        {
+            throw new InvalidOperationException("unexpected implicit primitive encoding");
+        }
+
+        internal virtual Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+        {
+            throw new InvalidOperationException("unexpected implicit constructed encoding");
+        }
+
+        /// <exception cref="IOException"/>
+        internal Asn1Object FromByteArray(byte[] bytes)
+        {
+            return CheckedCast(Asn1Object.FromByteArray(bytes));
+        }
+
+        internal Asn1Object GetContextInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            if (Asn1Tags.ContextSpecific != taggedObject.TagClass)
+                throw new InvalidOperationException("this method only valid for CONTEXT_SPECIFIC tags");
+
+            return CheckedCast(taggedObject.GetBaseUniversal(declaredExplicit, this));
+        }
+
+        internal Asn1Tag Tag
+        {
+            get { return m_tag; }
+        }
+    }
+}
diff --git a/crypto/src/asn1/Asn1UniversalTypes.cs b/crypto/src/asn1/Asn1UniversalTypes.cs
new file mode 100644
index 000000000..214918bcd
--- /dev/null
+++ b/crypto/src/asn1/Asn1UniversalTypes.cs
@@ -0,0 +1,74 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal sealed class Asn1UniversalTypes
+    {
+        private Asn1UniversalTypes()
+        {
+        }
+
+        internal static Asn1UniversalType Get(int tagNo)
+        {
+            switch (tagNo)
+            {
+            case Asn1Tags.Boolean:
+                return DerBoolean.Meta.Instance;
+            case Asn1Tags.Integer:
+                return DerInteger.Meta.Instance;
+            case Asn1Tags.BitString:
+                return DerBitString.Meta.Instance;
+            case Asn1Tags.OctetString:
+                return Asn1OctetString.Meta.Instance;
+            case Asn1Tags.Null:
+                return Asn1Null.Meta.Instance;
+            case Asn1Tags.ObjectIdentifier:
+                return DerObjectIdentifier.Meta.Instance;
+            case Asn1Tags.ObjectDescriptor:         // [UNIVERSAL 7] IMPLICIT GraphicString
+                return Asn1ObjectDescriptor.Meta.Instance;
+            case Asn1Tags.External:
+                return DerExternal.Meta.Instance;
+            case Asn1Tags.Enumerated:
+                return DerEnumerated.Meta.Instance;
+            case Asn1Tags.Utf8String:               // [UNIVERSAL 12] IMPLICIT OCTET STRING (encode as if)
+                return DerUtf8String.Meta.Instance;
+            case Asn1Tags.RelativeOid:
+                return Asn1RelativeOid.Meta.Instance;
+            case Asn1Tags.Sequence:
+                return Asn1Sequence.Meta.Instance;
+            case Asn1Tags.Set:
+                return Asn1Set.Meta.Instance;
+            case Asn1Tags.NumericString:            // [UNIVERSAL 18] IMPLICIT OCTET STRING (encode as if)
+                return DerNumericString.Meta.Instance;
+            case Asn1Tags.PrintableString:          // [UNIVERSAL 19] IMPLICIT OCTET STRING (encode as if)
+                return DerPrintableString.Meta.Instance;
+            case Asn1Tags.T61String:                // [UNIVERSAL 20] IMPLICIT OCTET STRING (encode as if)
+                return DerT61String.Meta.Instance;
+            case Asn1Tags.VideotexString:           // [UNIVERSAL 21] IMPLICIT OCTET STRING (encode as if)
+                return DerVideotexString.Meta.Instance;
+            case Asn1Tags.IA5String:                // [UNIVERSAL 22] IMPLICIT OCTET STRING (encode as if)
+                return DerIA5String.Meta.Instance;
+            case Asn1Tags.UtcTime:                  // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values)
+                return DerUtcTime.Meta.Instance;
+            case Asn1Tags.GeneralizedTime:          // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values)
+                return DerGeneralizedTime.Meta.Instance;
+            case Asn1Tags.GraphicString:            // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if)
+                return DerGraphicString.Meta.Instance;
+            case Asn1Tags.VisibleString:            // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if)
+                return DerVisibleString.Meta.Instance;
+            case Asn1Tags.GeneralString:            // [UNIVERSAL 27] IMPLICIT OCTET STRING (encode as if)
+                return DerGeneralString.Meta.Instance;
+            case Asn1Tags.UniversalString:          // [UNIVERSAL 28] IMPLICIT OCTET STRING (encode as if)
+                return DerUniversalString.Meta.Instance;
+            case Asn1Tags.BmpString:                // [UNIVERSAL 30] IMPLICIT OCTET STRING (encode as if)
+                return DerBmpString.Meta.Instance;
+
+            case Asn1Tags.Real:
+            case Asn1Tags.EmbeddedPdv:
+            case Asn1Tags.UnrestrictedString:
+            default:
+                return null;
+            }
+        }
+    }
+}
diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs
index fe6db2df7..7b014ef00 100644
--- a/crypto/src/asn1/Asn1Utilities.cs
+++ b/crypto/src/asn1/Asn1Utilities.cs
@@ -4,6 +4,23 @@ namespace Org.BouncyCastle.Asn1
 {
     public abstract class Asn1Utilities
     {
+        internal static Asn1TaggedObject CheckTag(Asn1TaggedObject taggedObject, int tagClass, int tagNo)
+        {
+            if (!taggedObject.HasTag(tagClass, tagNo))
+            {
+                string expected = GetTagText(tagClass, tagNo);
+                string found = GetTagText(taggedObject);
+                throw new InvalidOperationException("Expected " + expected + " tag but found " + found);
+            }
+            return taggedObject;
+        }
+
+
+        internal static string GetTagText(Asn1Tag tag)
+        {
+            return GetTagText(tag.TagClass, tag.TagNo);
+        }
+
         public static string GetTagText(Asn1TaggedObject taggedObject)
         {
             return GetTagText(taggedObject.TagClass, taggedObject.TagNo);
@@ -24,20 +41,14 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
+
         /*
          * Wrappers for Asn1TaggedObject.GetExplicitBaseObject
          */
 
         public static Asn1Encodable GetExplicitBaseObject(Asn1TaggedObject taggedObject, int tagClass, int tagNo)
         {
-            if (!taggedObject.HasTag(tagClass, tagNo))
-            {
-                string expected = GetTagText(tagClass, tagNo);
-                string found = GetTagText(taggedObject);
-                throw new InvalidOperationException("Expected " + expected + " tag but found " + found);
-            }
-
-            return taggedObject.GetExplicitBaseObject();
+            return CheckTag(taggedObject, tagClass, tagNo).GetExplicitBaseObject();
         }
 
         public static Asn1Encodable GetExplicitContextBaseObject(Asn1TaggedObject taggedObject, int tagNo)
@@ -65,14 +76,7 @@ namespace Org.BouncyCastle.Asn1
 
         public static Asn1TaggedObject GetExplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo)
         {
-            if (!taggedObject.HasTag(tagClass, tagNo))
-            {
-                string expected = GetTagText(tagClass, tagNo);
-                string found = GetTagText(taggedObject);
-                throw new InvalidOperationException("Expected " + expected + " tag but found " + found);
-            }
-
-            return taggedObject.GetExplicitBaseTagged();
+            return CheckTag(taggedObject, tagClass, tagNo).GetExplicitBaseTagged();
         }
 
         public static Asn1TaggedObject GetExplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo)
@@ -92,5 +96,73 @@ namespace Org.BouncyCastle.Asn1
         {
             return TryGetExplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo);
         }
+
+
+        /*
+         * Wrappers for Asn1TaggedObject.GetImplicitBaseTagged
+         */
+
+        public static Asn1TaggedObject GetImplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo,
+            int baseTagClass, int baseTagNo)
+        {
+            return CheckTag(taggedObject, tagClass, tagNo).GetImplicitBaseTagged(baseTagClass, baseTagNo);
+        }
+
+        public static Asn1TaggedObject GetImplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo,
+            int baseTagClass, int baseTagNo)
+        {
+            return GetImplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo, baseTagClass, baseTagNo);
+        }
+
+        public static Asn1TaggedObject TryGetImplicitBaseTagged(Asn1TaggedObject taggedObject, int tagClass, int tagNo,
+            int baseTagClass, int baseTagNo)
+        {
+            if (!taggedObject.HasTag(tagClass, tagNo))
+            {
+                return null;
+            }
+
+            return taggedObject.GetImplicitBaseTagged(baseTagClass, baseTagNo);
+        }
+
+        public static Asn1TaggedObject TryGetImplicitContextBaseTagged(Asn1TaggedObject taggedObject, int tagNo,
+            int baseTagClass, int baseTagNo)
+        {
+            return TryGetImplicitBaseTagged(taggedObject, Asn1Tags.ContextSpecific, tagNo, baseTagClass, baseTagNo);
+        }
+
+
+        /*
+         * Wrappers for Asn1TaggedObject.GetBaseUniversal
+         */
+
+        public static Asn1Object GetBaseUniversal(Asn1TaggedObject taggedObject, int tagClass, int tagNo,
+            bool declaredExplicit, int baseTagNo)
+        {
+            return CheckTag(taggedObject, tagClass, tagNo).GetBaseUniversal(declaredExplicit, baseTagNo);
+        }
+
+        public static Asn1Object GetContextBaseUniversal(Asn1TaggedObject taggedObject, int tagNo,
+            bool declaredExplicit, int baseTagNo)
+        {
+            return GetBaseUniversal(taggedObject, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo);
+        }
+
+        public static Asn1Object TryGetBaseUniversal(Asn1TaggedObject taggedObject, int tagClass, int tagNo,
+            bool declaredExplicit, int baseTagNo)
+        {
+            if (!taggedObject.HasTag(tagClass, tagNo))
+            {
+                return null;
+            }
+
+            return taggedObject.GetBaseUniversal(declaredExplicit, baseTagNo);
+        }
+
+        public static Asn1Object TryGetContextBaseUniversal(Asn1TaggedObject taggedObject, int tagNo,
+            bool declaredExplicit, int baseTagNo)
+        {
+            return TryGetBaseUniversal(taggedObject, Asn1Tags.ContextSpecific, tagNo, declaredExplicit, baseTagNo);
+        }
     }
 }
diff --git a/crypto/src/asn1/DERExternal.cs b/crypto/src/asn1/DERExternal.cs
index 67dad0534..28d5cc46a 100644
--- a/crypto/src/asn1/DERExternal.cs
+++ b/crypto/src/asn1/DERExternal.cs
@@ -11,6 +11,18 @@ namespace Org.BouncyCastle.Asn1
 	public class DerExternal
 		: Asn1Object
 	{
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerExternal), Asn1Tags.External) {}
+
+            internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+            {
+                return sequence.ToAsn1External();
+            }
+        }
+
         public static DerExternal GetInstance(object obj)
         {
             if (obj == null || obj is DerExternal)
@@ -27,7 +39,7 @@ namespace Org.BouncyCastle.Asn1
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (DerExternal)Meta.Instance.FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -38,16 +50,9 @@ namespace Org.BouncyCastle.Asn1
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
         }
 
-        public static DerExternal GetInstance(Asn1TaggedObject taggedObject, bool isExplicit)
+        public static DerExternal GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            if (isExplicit || baseObject is DerExternal)
-            {
-                return GetInstance(baseObject);
-            }
-
-            return Asn1Sequence.GetInstance(baseObject).ToAsn1External();
+            return (DerExternal)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
 		private readonly DerObjectIdentifier directReference;
diff --git a/crypto/src/asn1/DerApplicationSpecific.cs b/crypto/src/asn1/DerApplicationSpecific.cs
index 13b725325..15a4cdcc2 100644
--- a/crypto/src/asn1/DerApplicationSpecific.cs
+++ b/crypto/src/asn1/DerApplicationSpecific.cs
@@ -94,6 +94,7 @@ namespace Org.BouncyCastle.Asn1
             get { return m_taggedObject.TagNo; }
         }
 
+        [Obsolete("Will be removed")]
         public byte[] GetContents()
         {
             return m_taggedObject.GetContents();
@@ -112,21 +113,7 @@ namespace Org.BouncyCastle.Asn1
 
         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);
+            return m_taggedObject.GetBaseUniversal(false, tagNo);
         }
 
         public bool HasApplicationTag(int tagNo)
@@ -186,34 +173,6 @@ namespace Org.BouncyCastle.Asn1
             return m_taggedObject.GetEncodingImplicit(encoding, tagClass, tagNo);
         }
 
-        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++];
-
-                // 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");
-
-                while ((b & 0x80) != 0)
-                {
-                    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;
-        }
-
         private static int CheckTagClass(int tagClass)
         {
             if (Asn1Tags.Application != tagClass)
diff --git a/crypto/src/asn1/DerBMPString.cs b/crypto/src/asn1/DerBMPString.cs
index 4e35dfff8..a289eed1b 100644
--- a/crypto/src/asn1/DerBMPString.cs
+++ b/crypto/src/asn1/DerBMPString.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -10,7 +11,17 @@ namespace Org.BouncyCastle.Asn1
     public class DerBmpString
 		: DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerBmpString), Asn1Tags.BmpString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
 		/**
          * return a BMP string from the given object.
@@ -18,13 +29,29 @@ namespace Org.BouncyCastle.Asn1
          * @param obj the object we want converted.
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerBmpString GetInstance(
-            object obj)
+        public static DerBmpString GetInstance(object obj)
         {
             if (obj == null || obj is DerBmpString)
             {
                 return (DerBmpString)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerBmpString)
+                    return (DerBmpString)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerBmpString)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct BMP string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
@@ -32,48 +59,35 @@ namespace Org.BouncyCastle.Asn1
         /**
          * return a BMP string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *              be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerBmpString GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerBmpString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
-
-			if (isExplicit || o is DerBmpString)
-			{
-				return GetInstance(o);
-			}
-
-			return new DerBmpString(Asn1OctetString.GetInstance(o).GetOctets());
+            return (DerBmpString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
-		/**
-         * basic constructor - byte encoded string.
-         */
-        [Obsolete("Will become internal")]
-        public DerBmpString(byte[] str)
+        private readonly string m_str;
+
+        internal DerBmpString(byte[] contents)
         {
-			if (str == null)
-				throw new ArgumentNullException("str");
+			if (null == contents)
+				throw new ArgumentNullException("contents");
 
-            int byteLen = str.Length;
+            int byteLen = contents.Length;
             if (0 != (byteLen & 1))
-                throw new ArgumentException("malformed BMPString encoding encountered", "str");
+                throw new ArgumentException("malformed BMPString encoding encountered", "contents");
 
             int charLen = byteLen / 2;
             char[] cs = new char[charLen];
 
             for (int i = 0; i != charLen; i++)
             {
-                cs[i] = (char)((str[2 * i] << 8) | (str[2 * i + 1] & 0xff));
+                cs[i] = (char)((contents[2 * i] << 8) | (contents[2 * i + 1] & 0xff));
             }
 
-            this.str = new string(cs);
+            m_str = new string(cs);
         }
 
         internal DerBmpString(char[] str)
@@ -81,7 +95,7 @@ namespace Org.BouncyCastle.Asn1
             if (str == null)
                 throw new ArgumentNullException("str");
 
-            this.str = new string(str);
+            m_str = new string(str);
         }
 
         /**
@@ -92,23 +106,24 @@ namespace Org.BouncyCastle.Asn1
 			if (str == null)
 				throw new ArgumentNullException("str");
 
-            this.str = str;
+            m_str = str;
         }
 
         public override string GetString()
         {
-            return str;
+            return m_str;
         }
 
-		protected override bool Asn1Equals(
-			Asn1Object asn1Object)
+        protected override bool Asn1Equals(Asn1Object asn1Object)
         {
-			DerBmpString other = asn1Object as DerBmpString;
-
-			if (other == null)
-				return false;
+            DerBmpString that = asn1Object as DerBmpString;
+            return null != that
+                && this.m_str.Equals(that.m_str);
+        }
 
-			return this.str.Equals(other.str);
+        protected override int Asn1GetHashCode()
+        {
+            return m_str.GetHashCode();
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
@@ -123,7 +138,7 @@ namespace Org.BouncyCastle.Asn1
 
         private byte[] GetContents()
         {
-            char[] c = str.ToCharArray();
+            char[] c = m_str.ToCharArray();
             byte[] b = new byte[c.Length * 2];
 
             for (int i = 0; i != c.Length; i++)
@@ -134,5 +149,16 @@ namespace Org.BouncyCastle.Asn1
 
             return b;
         }
+
+        internal static DerBmpString CreatePrimitive(byte[] contents)
+        {
+            return new DerBmpString(contents);
+        }
+
+        internal static DerBmpString CreatePrimitive(char[] str)
+        {
+            // TODO[asn1] Asn1InputStream has a validator/converter that should be unified in this class somehow
+            return new DerBmpString(str);
+        }
     }
 }
diff --git a/crypto/src/asn1/DerBitString.cs b/crypto/src/asn1/DerBitString.cs
index 375abb479..4596fbbc8 100644
--- a/crypto/src/asn1/DerBitString.cs
+++ b/crypto/src/asn1/DerBitString.cs
@@ -11,7 +11,24 @@ namespace Org.BouncyCastle.Asn1
 	public class DerBitString
 		: DerStringBase
 	{
-		private static readonly char[] table
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerBitString), Asn1Tags.BitString) { }
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+
+            internal override Asn1Object FromImplicitConstructed(Asn1Sequence sequence)
+            {
+                return sequence.ToAsn1BitString();
+            }
+        }
+
+        private static readonly char[] table
 			= { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
         /**
@@ -323,9 +340,7 @@ namespace Org.BouncyCastle.Asn1
 
                 byte finalOctet = contents[length - 1];
                 if (finalOctet != (byte)(finalOctet & (0xFF << padBits)))
-                {
                     return new BerBitString(contents, false);
-                }
             }
 
             return new DerBitString(contents, false);
diff --git a/crypto/src/asn1/DerBoolean.cs b/crypto/src/asn1/DerBoolean.cs
index 29e2d8bd9..ad578ae80 100644
--- a/crypto/src/asn1/DerBoolean.cs
+++ b/crypto/src/asn1/DerBoolean.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -7,7 +8,17 @@ namespace Org.BouncyCastle.Asn1
     public class DerBoolean
         : Asn1Object
     {
-        private readonly byte value;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerBoolean), Asn1Tags.Boolean) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
         public static readonly DerBoolean False = new DerBoolean(false);
         public static readonly DerBoolean True  = new DerBoolean(true);
@@ -17,49 +28,57 @@ namespace Org.BouncyCastle.Asn1
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerBoolean GetInstance(
-            object obj)
+        public static DerBoolean GetInstance(object obj)
         {
             if (obj == null || obj is DerBoolean)
             {
-                return (DerBoolean) obj;
+                return (DerBoolean)obj;
+            }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerBoolean)
+                    return (DerBoolean)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerBoolean)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct boolean from byte[]: " + e.Message);
+                }
             }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
-        /**
-         * return a DerBoolean from the passed in bool.
-         */
-        public static DerBoolean GetInstance(
-            bool value)
+        public static DerBoolean GetInstance(bool value)
         {
             return value ? True : False;
         }
 
+        public static DerBoolean GetInstance(int value)
+        {
+            return value != 0 ? True : False;
+        }
+
         /**
          * return a Boolean from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerBoolean GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerBoolean GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object o = obj.GetObject();
-
-            if (isExplicit || o is DerBoolean)
-            {
-                return GetInstance(o);
-            }
-
-            return FromOctetString(((Asn1OctetString)o).GetOctets());
+            return (DerBoolean)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
+        private readonly byte value;
+
         public DerBoolean(
             byte[] val)
         {
@@ -112,16 +131,14 @@ namespace Org.BouncyCastle.Asn1
             return IsTrue ? "TRUE" : "FALSE";
         }
 
-        internal static DerBoolean FromOctetString(byte[] value)
+        internal static DerBoolean CreatePrimitive(byte[] contents)
         {
-            if (value.Length != 1)
-            {
-                throw new ArgumentException("BOOLEAN value should have 1 byte in it", "value");
-            }
+            if (contents.Length != 1)
+                throw new ArgumentException("BOOLEAN value should have 1 byte in it", "contents");
 
-            byte b = value[0];
+            byte b = contents[0];
 
-            return b == 0 ? False : b == 0xFF ? True : new DerBoolean(value);
+            return b == 0 ? False : b == 0xFF ? True : new DerBoolean(contents);
         }
 
         private byte[] GetContents(int encoding)
diff --git a/crypto/src/asn1/DerEnumerated.cs b/crypto/src/asn1/DerEnumerated.cs
index 6fd2f9b62..920b3dc8e 100644
--- a/crypto/src/asn1/DerEnumerated.cs
+++ b/crypto/src/asn1/DerEnumerated.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
@@ -8,21 +9,46 @@ namespace Org.BouncyCastle.Asn1
     public class DerEnumerated
         : Asn1Object
     {
-        private readonly byte[] bytes;
-        private readonly int start;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerEnumerated), Asn1Tags.Enumerated) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets(), false);
+            }
+        }
 
         /**
          * return an integer from the passed in object
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerEnumerated GetInstance(
-            object obj)
+        public static DerEnumerated GetInstance(object obj)
         {
             if (obj == null || obj is DerEnumerated)
             {
                 return (DerEnumerated)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerEnumerated)
+                    return (DerEnumerated)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerEnumerated)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct enumerated from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
@@ -30,32 +56,24 @@ namespace Org.BouncyCastle.Asn1
         /**
          * return an Enumerated from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerEnumerated GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerEnumerated GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object o = obj.GetObject();
-
-            if (isExplicit || o is DerEnumerated)
-            {
-                return GetInstance(o);
-            }
-
-            return FromOctetString(((Asn1OctetString)o).GetOctets());
+            return (DerEnumerated)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
+        private readonly byte[] contents;
+        private readonly int start;
+
         public DerEnumerated(int val)
         {
             if (val < 0)
                 throw new ArgumentException("enumerated must be non-negative", "val");
 
-            this.bytes = BigInteger.ValueOf(val).ToByteArray();
+            this.contents = BigInteger.ValueOf(val).ToByteArray();
             this.start = 0;
         }
 
@@ -64,7 +82,7 @@ namespace Org.BouncyCastle.Asn1
             if (val < 0L)
                 throw new ArgumentException("enumerated must be non-negative", "val");
 
-            this.bytes = BigInteger.ValueOf(val).ToByteArray();
+            this.contents = BigInteger.ValueOf(val).ToByteArray();
             this.start = 0;
         }
 
@@ -73,37 +91,42 @@ namespace Org.BouncyCastle.Asn1
             if (val.SignValue < 0)
                 throw new ArgumentException("enumerated must be non-negative", "val");
 
-            this.bytes = val.ToByteArray();
+            this.contents = val.ToByteArray();
             this.start = 0;
         }
 
-        public DerEnumerated(byte[] bytes)
+        public DerEnumerated(byte[] contents)
+            : this(contents, true)
+        {
+        }
+
+        internal DerEnumerated(byte[] contents, bool clone)
         {
-            if (DerInteger.IsMalformed(bytes))
-                throw new ArgumentException("malformed enumerated", "bytes");
-            if (0 != (bytes[0] & 0x80))
-                throw new ArgumentException("enumerated must be non-negative", "bytes");
+            if (DerInteger.IsMalformed(contents))
+                throw new ArgumentException("malformed enumerated", "contents");
+            if (0 != (contents[0] & 0x80))
+                throw new ArgumentException("enumerated must be non-negative", "contents");
 
-            this.bytes = Arrays.Clone(bytes);
-            this.start = DerInteger.SignBytesToSkip(bytes);
+            this.contents = clone ? Arrays.Clone(contents) : contents;
+            this.start = DerInteger.SignBytesToSkip(this.contents);
         }
 
         public BigInteger Value
         {
-            get { return new BigInteger(bytes); }
+            get { return new BigInteger(contents); }
         }
 
         public bool HasValue(int x)
         {
-            return (bytes.Length - start) <= 4
-                && DerInteger.IntValue(bytes, start, DerInteger.SignExtSigned) == x;
+            return (contents.Length - start) <= 4
+                && DerInteger.IntValue(contents, start, DerInteger.SignExtSigned) == x;
         }
 
         public bool HasValue(BigInteger x)
         {
             return null != x
                 // Fast check to avoid allocation
-                && DerInteger.IntValue(bytes, start, DerInteger.SignExtSigned) == x.IntValue
+                && DerInteger.IntValue(contents, start, DerInteger.SignExtSigned) == x.IntValue
                 && Value.Equals(x);
         }
 
@@ -111,22 +134,22 @@ namespace Org.BouncyCastle.Asn1
         {
             get
             {
-                int count = bytes.Length - start;
+                int count = contents.Length - start;
                 if (count > 4)
                     throw new ArithmeticException("ASN.1 Enumerated out of int range");
 
-                return DerInteger.IntValue(bytes, start, DerInteger.SignExtSigned);
+                return DerInteger.IntValue(contents, start, DerInteger.SignExtSigned);
             }
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Enumerated, bytes);
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Enumerated, contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, bytes);
+            return new PrimitiveEncoding(tagClass, tagNo, contents);
         }
 
         protected override bool Asn1Equals(Asn1Object asn1Object)
@@ -135,31 +158,31 @@ namespace Org.BouncyCastle.Asn1
             if (other == null)
                 return false;
 
-            return Arrays.AreEqual(this.bytes, other.bytes);
+            return Arrays.AreEqual(this.contents, other.contents);
         }
 
         protected override int Asn1GetHashCode()
         {
-            return Arrays.GetHashCode(bytes);
+            return Arrays.GetHashCode(contents);
         }
 
         private static readonly DerEnumerated[] cache = new DerEnumerated[12];
 
-        internal static DerEnumerated FromOctetString(byte[] enc)
+        internal static DerEnumerated CreatePrimitive(byte[] contents, bool clone)
         {
-            if (enc.Length > 1)
-                return new DerEnumerated(enc);
-            if (enc.Length == 0)
-                throw new ArgumentException("ENUMERATED has zero length", "enc");
+            if (contents.Length > 1)
+                return new DerEnumerated(contents, clone);
+            if (contents.Length == 0)
+                throw new ArgumentException("ENUMERATED has zero length", "contents");
 
-            int value = enc[0];
+            int value = contents[0];
             if (value >= cache.Length)
-                return new DerEnumerated(enc);
+                return new DerEnumerated(contents, clone);
 
             DerEnumerated possibleMatch = cache[value];
             if (possibleMatch == null)
             {
-                cache[value] = possibleMatch = new DerEnumerated(enc);
+                cache[value] = possibleMatch = new DerEnumerated(contents, clone);
             }
             return possibleMatch;
         }
diff --git a/crypto/src/asn1/DerGeneralString.cs b/crypto/src/asn1/DerGeneralString.cs
index f2a47c370..e6637732a 100644
--- a/crypto/src/asn1/DerGeneralString.cs
+++ b/crypto/src/asn1/DerGeneralString.cs
@@ -1,5 +1,5 @@
 using System;
-using System.Text;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -8,78 +8,108 @@ namespace Org.BouncyCastle.Asn1
     public class DerGeneralString
         : DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType 
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerGeneralString), Asn1Tags.GeneralString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
-        public static DerGeneralString GetInstance(
-            object obj)
+        public static DerGeneralString GetInstance(object obj)
         {
             if (obj == null || obj is DerGeneralString)
             {
                 return (DerGeneralString) obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerGeneralString)
+                    return (DerGeneralString)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerGeneralString)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct general string from byte[]: " + e.Message);
+                }
+            }
 
-			throw new ArgumentException("illegal object in GetInstance: "
-                    + Platform.GetTypeName(obj));
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
-        public static DerGeneralString GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerGeneralString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
+            return (DerGeneralString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
 
-			if (isExplicit || o is DerGeneralString)
-			{
-				return GetInstance(o);
-			}
+        private readonly byte[] m_contents;
 
-			return new DerGeneralString(((Asn1OctetString)o).GetOctets());
+		public DerGeneralString(string str)
+        {
+			if (str == null)
+				throw new ArgumentNullException("str");
+
+			m_contents = Strings.ToAsciiByteArray(str);
         }
 
-        public DerGeneralString(
-			byte[] str)
-			: this(Strings.FromAsciiByteArray(str))
+        public DerGeneralString(byte[] contents)
+            : this(contents, true)
         {
         }
 
-		public DerGeneralString(
-			string str)
+        internal DerGeneralString(byte[] contents, bool clone)
         {
-			if (str == null)
-				throw new ArgumentNullException("str");
+            if (null == contents)
+                throw new ArgumentNullException("contents");
 
-			this.str = str;
+            m_contents = clone ? Arrays.Clone(contents) : contents;
         }
 
         public override string GetString()
         {
-            return str;
+            return Strings.FromAsciiByteArray(m_contents);
         }
 
 		public byte[] GetOctets()
         {
-            return Strings.ToAsciiByteArray(str);
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralString, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralString, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
-        protected override bool Asn1Equals(
-			Asn1Object asn1Object)
+        protected override bool Asn1Equals(Asn1Object asn1Object)
         {
-			DerGeneralString other = asn1Object as DerGeneralString;
+            DerGeneralString that = asn1Object as DerGeneralString;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
 
-			if (other == null)
-				return false;
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
+        }
 
-			return this.str.Equals(other.str);
+        internal static DerGeneralString CreatePrimitive(byte[] contents)
+        {
+            return new DerGeneralString(contents, false);
         }
     }
 }
diff --git a/crypto/src/asn1/DerGeneralizedTime.cs b/crypto/src/asn1/DerGeneralizedTime.cs
index 41c897751..ed2cb5e62 100644
--- a/crypto/src/asn1/DerGeneralizedTime.cs
+++ b/crypto/src/asn1/DerGeneralizedTime.cs
@@ -1,5 +1,6 @@
 using System;
 using System.Globalization;
+using System.IO;
 using System.Text;
 
 using Org.BouncyCastle.Utilities;
@@ -12,47 +13,64 @@ namespace Org.BouncyCastle.Asn1
     public class DerGeneralizedTime
         : Asn1Object
     {
-        private readonly string time;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerGeneralizedTime), Asn1Tags.GeneralizedTime) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
         /**
          * return a generalized time from the passed in object
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerGeneralizedTime GetInstance(
-            object obj)
+        public static DerGeneralizedTime GetInstance(object obj)
         {
             if (obj == null || obj is DerGeneralizedTime)
             {
                 return (DerGeneralizedTime)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerGeneralizedTime)
+                    return (DerGeneralizedTime)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerGeneralizedTime)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
         }
 
         /**
-         * return a Generalized Time object from a tagged object.
+         * return a generalized Time object from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerGeneralizedTime GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerGeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object o = obj.GetObject();
-
-            if (isExplicit || o is DerGeneralizedTime)
-            {
-                return GetInstance(o);
-            }
-
-            return new DerGeneralizedTime(((Asn1OctetString)o).GetOctets());
+            return (DerGeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
+        private readonly string time;
+
         /**
          * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
          * for local time, or Z+-HHMM on the end, for difference between local
@@ -305,20 +323,21 @@ namespace Org.BouncyCastle.Asn1
             return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
         }
 
-        protected override bool Asn1Equals(
-            Asn1Object asn1Object)
+        protected override bool Asn1Equals(Asn1Object asn1Object)
         {
-            DerGeneralizedTime other = asn1Object as DerGeneralizedTime;
-
-            if (other == null)
-                return false;
-
-            return this.time.Equals(other.time);
+            DerGeneralizedTime that = asn1Object as DerGeneralizedTime;
+            return null != that
+                && this.time.Equals(that.time);
         }
 
         protected override int Asn1GetHashCode()
         {
             return time.GetHashCode();
         }
+
+        internal static DerGeneralizedTime CreatePrimitive(byte[] contents)
+        {
+            return new DerGeneralizedTime(contents);
+        }
     }
 }
diff --git a/crypto/src/asn1/DerGraphicString.cs b/crypto/src/asn1/DerGraphicString.cs
index 52ccb7e93..cb32d14eb 100644
--- a/crypto/src/asn1/DerGraphicString.cs
+++ b/crypto/src/asn1/DerGraphicString.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -7,6 +8,18 @@ namespace Org.BouncyCastle.Asn1
     public class DerGraphicString
         : DerStringBase
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerGraphicString), Asn1Tags.GraphicString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
+
         /**
          * return a Graphic String from the passed in object
          *
@@ -20,16 +33,21 @@ namespace Org.BouncyCastle.Asn1
             {
                 return (DerGraphicString)obj;
             }
-
-            if (obj is byte[])
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerGraphicString)
+                    return (DerGraphicString)asn1Object;
+            }
+            else if (obj is byte[])
             {
                 try
                 {
-                    return (DerGraphicString)FromByteArray((byte[])obj);
+                    return (DerGraphicString)Meta.Instance.FromByteArray((byte[])obj);
                 }
-                catch (Exception e)
+                catch (IOException e)
                 {
-                    throw new ArgumentException("encoding error in GetInstance: " + e.ToString(), "obj");
+                    throw new ArgumentException("failed to construct graphic string from byte[]: " + e.Message);
                 }
             }
 
@@ -39,23 +57,14 @@ namespace Org.BouncyCastle.Asn1
         /**
          * return a Graphic String from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicit true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception IllegalArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception IllegalArgumentException if the tagged object cannot be converted.
          * @return a DerGraphicString instance, or null.
          */
-        public static DerGraphicString GetInstance(Asn1TaggedObject obj, bool isExplicit)
+        public static DerGraphicString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
-
-            if (isExplicit || o is DerGraphicString)
-			{
-				return GetInstance(o);
-			}
-
-            return new DerGraphicString(((Asn1OctetString)o).GetOctets());
+            return (DerGraphicString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         private readonly byte[] m_contents;
diff --git a/crypto/src/asn1/DerIA5String.cs b/crypto/src/asn1/DerIA5String.cs
index d608930fd..a56879831 100644
--- a/crypto/src/asn1/DerIA5String.cs
+++ b/crypto/src/asn1/DerIA5String.cs
@@ -1,30 +1,56 @@
 using System;
-using System.Text;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1
 {
     /**
-     * Der IA5String object - this is an ascii string.
+     * IA5String object - this is an Ascii string.
      */
     public class DerIA5String
         : DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerIA5String), Asn1Tags.IA5String) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
         /**
-         * return a IA5 string from the passed in object
+         * return an IA5 string from the passed in object
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerIA5String GetInstance(
-            object obj)
+        public static DerIA5String GetInstance(object obj)
         {
             if (obj == null || obj is DerIA5String)
             {
                 return (DerIA5String)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerIA5String)
+                    return (DerIA5String)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerIA5String)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct IA5 string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
@@ -32,40 +58,18 @@ namespace Org.BouncyCastle.Asn1
         /**
          * return an IA5 string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerIA5String GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerIA5String GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
-
-			if (isExplicit || o is DerIA5String)
-			{
-				return GetInstance(o);
-			}
-
-			return new DerIA5String(((Asn1OctetString)o).GetOctets());
+            return (DerIA5String)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
-        /**
-         * basic constructor - with bytes.
-         */
-        public DerIA5String(
-            byte[] str)
-            : this(Strings.FromAsciiByteArray(str), false)
-        {
-        }
+        private readonly byte[] m_contents;
 
-		/**
-		* basic constructor - without validation.
-		*/
-		public DerIA5String(
-			string str)
+		public DerIA5String(string str)
 			: this(str, false)
 		{
 		}
@@ -78,72 +82,81 @@ namespace Org.BouncyCastle.Asn1
 		* @throws ArgumentException if validate is true and the string
 		* contains characters that should not be in an IA5String.
 		*/
-		public DerIA5String(
-			string	str,
-			bool	validate)
+		public DerIA5String(string str, bool validate)
 		{
 			if (str == null)
 				throw new ArgumentNullException("str");
 			if (validate && !IsIA5String(str))
 				throw new ArgumentException("string contains illegal characters", "str");
 
-			this.str = str;
+			m_contents = Strings.ToAsciiByteArray(str);
 		}
 
-		public override string GetString()
+        public DerIA5String(byte[] contents)
+            : this(contents, true)
+        {
+        }
+
+        internal DerIA5String(byte[] contents, bool clone)
+        {
+            if (null == contents)
+                throw new ArgumentNullException("contents");
+
+            m_contents = clone ? Arrays.Clone(contents) : contents;
+        }
+
+        public override string GetString()
         {
-            return str;
+            return Strings.FromAsciiByteArray(m_contents);
         }
 
 		public byte[] GetOctets()
         {
-            return Strings.ToAsciiByteArray(str);
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.IA5String, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.IA5String, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
-        protected override int Asn1GetHashCode()
-		{
-            return this.str.GetHashCode();
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            DerIA5String that = asn1Object as DerIA5String;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
         }
 
-		protected override bool Asn1Equals(
-            Asn1Object asn1Object)
+        protected override int Asn1GetHashCode()
         {
-			DerIA5String other = asn1Object as DerIA5String;
-
-			if (other == null)
-				return false;
-
-			return this.str.Equals(other.str);
+            return Arrays.GetHashCode(m_contents);
         }
 
-		/**
+        /**
 		 * return true if the passed in String can be represented without
 		 * loss as an IA5String, false otherwise.
 		 *
 		 * @return true if in printable set, false otherwise.
 		 */
-		public static bool IsIA5String(
-			string str)
+        public static bool IsIA5String(string str)
 		{
 			foreach (char ch in str)
 			{
 				if (ch > 0x007f)
-				{
 					return false;
-				}
 			}
 
 			return true;
 		}
-	}
+
+        internal static DerIA5String CreatePrimitive(byte[] contents)
+        {
+            return new DerIA5String(contents, false);
+        }
+    }
 }
diff --git a/crypto/src/asn1/DerInteger.cs b/crypto/src/asn1/DerInteger.cs
index 8b112f693..c8d4e47df 100644
--- a/crypto/src/asn1/DerInteger.cs
+++ b/crypto/src/asn1/DerInteger.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
@@ -8,6 +9,18 @@ namespace Org.BouncyCastle.Asn1
     public class DerInteger
         : Asn1Object
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerInteger), Asn1Tags.Integer) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
+
         public const string AllowUnsafeProperty = "Org.BouncyCastle.Asn1.AllowUnsafeInteger";
 
         internal static bool AllowUnsafe()
@@ -27,13 +40,29 @@ namespace Org.BouncyCastle.Asn1
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerInteger GetInstance(
-            object obj)
+        public static DerInteger GetInstance(object obj)
         {
             if (obj == null || obj is DerInteger)
             {
                 return (DerInteger)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerInteger)
+                    return (DerInteger)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerInteger)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct integer from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
@@ -41,27 +70,13 @@ namespace Org.BouncyCastle.Asn1
         /**
          * return an Integer from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param isExplicit true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot  be converted.
          */
-        public static DerInteger GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerInteger GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            if (obj == null)
-                throw new ArgumentNullException("obj");
-
-			Asn1Object o = obj.GetObject();
-
-			if (isExplicit || o is DerInteger)
-			{
-				return GetInstance(o);
-			}
-
-			return new DerInteger(Asn1OctetString.GetInstance(o).GetOctets());
+            return (DerInteger)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
 		public DerInteger(int value)
@@ -198,6 +213,11 @@ namespace Org.BouncyCastle.Asn1
 			return Value.ToString();
 		}
 
+        internal static DerInteger CreatePrimitive(byte[] contents)
+        {
+            return new DerInteger(contents, false);
+        }
+
         internal static int IntValue(byte[] bytes, int start, int signExt)
         {
             int length = bytes.Length;
diff --git a/crypto/src/asn1/DerNumericString.cs b/crypto/src/asn1/DerNumericString.cs
index bec0c3a52..693ff7d6e 100644
--- a/crypto/src/asn1/DerNumericString.cs
+++ b/crypto/src/asn1/DerNumericString.cs
@@ -1,5 +1,5 @@
 using System;
-using System.Text;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -11,66 +11,70 @@ namespace Org.BouncyCastle.Asn1
     public class DerNumericString
         : DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerNumericString), Asn1Tags.NumericString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
         /**
-         * return a Numeric string from the passed in object
+         * return a numeric string from the passed in object
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerNumericString GetInstance(
-            object obj)
+        public static DerNumericString GetInstance(object obj)
         {
             if (obj == null || obj is DerNumericString)
             {
                 return (DerNumericString)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerNumericString)
+                    return (DerNumericString)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerNumericString)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct numeric string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return an Numeric string from a tagged object.
+         * return a numeric string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerNumericString GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerNumericString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
-
-			if (isExplicit || o is DerNumericString)
-			{
-				return GetInstance(o);
-			}
-
-			return new DerNumericString(Asn1OctetString.GetInstance(o).GetOctets());
+            return (DerNumericString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
-		/**
-		 * basic constructor - with bytes.
-		 */
-		public DerNumericString(
-			byte[] str)
-            : this(Strings.FromAsciiByteArray(str), false)
-		{
-		}
+        private readonly byte[] m_contents;
 
-		/**
-		 * basic constructor -  without validation..
-		 */
-		public DerNumericString(
-			string str)
-			: this(str, false)
-		{
-		}
+        public DerNumericString(string str)
+            : this(str, false)
+        {
+        }
 
-		/**
+        /**
 		* Constructor with optional validation.
 		*
 		* @param string the base string to wrap.
@@ -78,57 +82,68 @@ namespace Org.BouncyCastle.Asn1
 		* @throws ArgumentException if validate is true and the string
 		* contains characters that should not be in a NumericString.
 		*/
-		public DerNumericString(
-			string	str,
-			bool	validate)
-		{
-			if (str == null)
-				throw new ArgumentNullException("str");
-			if (validate && !IsNumericString(str))
-				throw new ArgumentException("string contains illegal characters", "str");
+        public DerNumericString(string str, bool validate)
+        {
+            if (str == null)
+                throw new ArgumentNullException("str");
+            if (validate && !IsNumericString(str))
+                throw new ArgumentException("string contains illegal characters", "str");
+
+            m_contents = Strings.ToAsciiByteArray(str);
+        }
 
-			this.str = str;
+        public DerNumericString(byte[] contents)
+            : this(contents, true)
+		{
 		}
 
-		public override string GetString()
+        internal DerNumericString(byte[] contents, bool clone)
+        {
+            if (null == contents)
+                throw new ArgumentNullException("contents");
+
+            m_contents = clone ? Arrays.Clone(contents) : contents;
+        }
+
+        public override string GetString()
         {
-            return str;
+            return Strings.FromAsciiByteArray(m_contents);
         }
 
 		public byte[] GetOctets()
         {
-            return Strings.ToAsciiByteArray(str);
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.NumericString, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.NumericString, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
-        protected override bool Asn1Equals(
-			Asn1Object asn1Object)
+        protected override bool Asn1Equals(Asn1Object asn1Object)
 		{
-			DerNumericString other = asn1Object as DerNumericString;
-
-			if (other == null)
-				return false;
+			DerNumericString that = asn1Object as DerNumericString;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
 
-			return this.str.Equals(other.str);
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
         }
 
-		/**
+        /**
 		 * Return true if the string can be represented as a NumericString ('0'..'9', ' ')
 		 *
 		 * @param str string to validate.
 		 * @return true if numeric, fale otherwise.
 		 */
-		public static bool IsNumericString(
-			string str)
+        public static bool IsNumericString(string str)
 		{
 			foreach (char ch in str)
 			{
@@ -138,5 +153,39 @@ namespace Org.BouncyCastle.Asn1
 
 			return true;
 		}
-	}
+
+        internal static bool IsNumericString(byte[] contents)
+        {
+            for (int i = 0; i < contents.Length; ++i)
+            {
+                switch (contents[i])
+                {
+                case 0x20:
+                case 0x30:
+                case 0x31:
+                case 0x32:
+                case 0x33:
+                case 0x34:
+                case 0x35:
+                case 0x36:
+                case 0x37:
+                case 0x38:
+                case 0x39:
+                    break;
+                default:
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        internal static DerNumericString CreatePrimitive(byte[] contents)
+        {
+            // TODO Validation - sort out exception types
+            //if (!IsNumericString(contents))
+
+            return new DerNumericString(contents, false);
+        }
+    }
 }
diff --git a/crypto/src/asn1/DerObjectIdentifier.cs b/crypto/src/asn1/DerObjectIdentifier.cs
index f91ad2150..8b77f966c 100644
--- a/crypto/src/asn1/DerObjectIdentifier.cs
+++ b/crypto/src/asn1/DerObjectIdentifier.cs
@@ -10,13 +10,25 @@ namespace Org.BouncyCastle.Asn1
     public class DerObjectIdentifier
         : Asn1Object
     {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerObjectIdentifier), Asn1Tags.ObjectIdentifier) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets(), false);
+            }
+        }
+
         public static DerObjectIdentifier FromContents(byte[] contents)
         {
             return CreatePrimitive(contents, true);
         }
 
         /**
-         * return an Oid from the passed in object
+         * return an OID from the passed in object
          *
          * @exception ArgumentException if the object cannot be converted.
          */
@@ -36,7 +48,7 @@ namespace Org.BouncyCastle.Asn1
             {
                 try
                 {
-                    return GetInstance(FromByteArray((byte[])obj));
+                    return (DerObjectIdentifier)Meta.Instance.FromByteArray((byte[])obj);
                 }
                 catch (IOException e)
                 {
@@ -49,14 +61,19 @@ namespace Org.BouncyCastle.Asn1
 
         public static DerObjectIdentifier GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object baseObject = taggedObject.GetObject();
-
-            if (declaredExplicit || baseObject is DerObjectIdentifier)
+            /*
+             * TODO[asn1] This block here is for backward compatibility, but should eventually be removed.
+             * 
+             * - see https://github.com/bcgit/bc-java/issues/1015
+             */
+            if (!declaredExplicit && !taggedObject.IsParsed())
             {
-                return GetInstance(baseObject);
+                Asn1Object baseObject = taggedObject.GetObject();
+                if (!(baseObject is DerObjectIdentifier))
+                    return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets());
             }
 
-            return FromContents(Asn1OctetString.GetInstance(baseObject).GetOctets());
+            return (DerObjectIdentifier)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
         private const long LongLimit = (Int64.MaxValue >> 7) - 0x7F;
diff --git a/crypto/src/asn1/DerPrintableString.cs b/crypto/src/asn1/DerPrintableString.cs
index 30358e219..3c44a2d52 100644
--- a/crypto/src/asn1/DerPrintableString.cs
+++ b/crypto/src/asn1/DerPrintableString.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -10,61 +11,65 @@ namespace Org.BouncyCastle.Asn1
     public class DerPrintableString
         : DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerPrintableString), Asn1Tags.PrintableString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
 		/**
          * return a printable string from the passed in object.
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerPrintableString GetInstance(
-            object obj)
+        public static DerPrintableString GetInstance(object obj)
         {
             if (obj == null || obj is DerPrintableString)
             {
                 return (DerPrintableString)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerPrintableString)
+                    return (DerPrintableString)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerPrintableString)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct printable string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return a Printable string from a tagged object.
+         * return a printable string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerPrintableString GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerPrintableString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
-
-			if (isExplicit || o is DerPrintableString)
-			{
-				return GetInstance(o);
-			}
-
-			return new DerPrintableString(Asn1OctetString.GetInstance(o).GetOctets());
+            return (DerPrintableString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
-        /**
-         * basic constructor - byte encoded string.
-         */
-        public DerPrintableString(
-            byte[] str)
-            : this(Strings.FromAsciiByteArray(str), false)
-        {
-        }
+        private readonly byte[] m_contents;
 
-		/**
-		 * basic constructor - this does not validate the string
-		 */
-		public DerPrintableString(
-			string str)
+		public DerPrintableString(string str)
 			: this(str, false)
 		{
 		}
@@ -77,57 +82,69 @@ namespace Org.BouncyCastle.Asn1
 		* @throws ArgumentException if validate is true and the string
 		* contains characters that should not be in a PrintableString.
 		*/
-		public DerPrintableString(
-			string	str,
-			bool	validate)
+		public DerPrintableString(string str, bool validate)
 		{
 			if (str == null)
 				throw new ArgumentNullException("str");
 			if (validate && !IsPrintableString(str))
 				throw new ArgumentException("string contains illegal characters", "str");
 
-			this.str = str;
+            m_contents = Strings.ToAsciiByteArray(str);
 		}
 
-		public override string GetString()
+        public DerPrintableString(byte[] contents)
+            : this(contents, true)
+        {
+        }
+
+        internal DerPrintableString(byte[] contents, bool clone)
+        {
+            if (null == contents)
+                throw new ArgumentNullException("contents");
+
+            m_contents = clone ? Arrays.Clone(contents) : contents;
+        }
+
+        public override string GetString()
         {
-            return str;
+            return Strings.FromAsciiByteArray(m_contents);
         }
 
-		public byte[] GetOctets()
+        public byte[] GetOctets()
         {
-            return Strings.ToAsciiByteArray(str);
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.PrintableString, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.PrintableString, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
         protected override bool Asn1Equals(
 			Asn1Object asn1Object)
 		{
-			DerPrintableString other = asn1Object as DerPrintableString;
-
-			if (other == null)
-				return false;
+            DerPrintableString that = asn1Object as DerPrintableString;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
 
-			return this.str.Equals(other.str);
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
         }
 
-		/**
+        /**
 		 * return true if the passed in String can be represented without
 		 * loss as a PrintableString, false otherwise.
 		 *
 		 * @return true if in printable set, false otherwise.
 		 */
-		public static bool IsPrintableString(
-			string str)
+        public static bool IsPrintableString(string str)
 		{
 			foreach (char ch in str)
 			{
@@ -162,5 +179,10 @@ namespace Org.BouncyCastle.Asn1
 
 			return true;
 		}
-	}
+
+        internal static DerPrintableString CreatePrimitive(byte[] contents)
+        {
+            return new DerPrintableString(contents, false);
+        }
+    }
 }
diff --git a/crypto/src/asn1/DerT61String.cs b/crypto/src/asn1/DerT61String.cs
index 64a9bd469..a0e4f1d22 100644
--- a/crypto/src/asn1/DerT61String.cs
+++ b/crypto/src/asn1/DerT61String.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -10,97 +11,120 @@ namespace Org.BouncyCastle.Asn1
     public class DerT61String
         : DerStringBase
     {
-		private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerT61String), Asn1Tags.T61String) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
 		/**
          * return a T61 string from the passed in object.
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerT61String GetInstance(
-            object obj)
+        public static DerT61String GetInstance(object obj)
         {
             if (obj == null || obj is DerT61String)
             {
                 return (DerT61String)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerT61String)
+                    return (DerT61String)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerT61String)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct T61 string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return an T61 string from a tagged object.
+         * return a T61 string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerT61String GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerT61String GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
+            return (DerT61String)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
 
-			if (isExplicit || o is DerT61String)
-			{
-				return GetInstance(o);
-			}
+        private readonly byte[] m_contents;
 
-			return new DerT61String(Asn1OctetString.GetInstance(o).GetOctets());
+        public DerT61String(string str)
+        {
+			if (str == null)
+				throw new ArgumentNullException("str");
+
+            m_contents = Strings.ToByteArray(str);
         }
 
-        /**
-         * basic constructor - with bytes.
-         */
-        public DerT61String(
-            byte[] str)
-			: this(Strings.FromByteArray(str))
-		{
+        public DerT61String(byte[] contents)
+            : this(contents, true)
+        {
         }
 
-		/**
-         * basic constructor - with string.
-         */
-        public DerT61String(
-            string str)
+        internal DerT61String(byte[] contents, bool clone)
         {
-			if (str == null)
-				throw new ArgumentNullException("str");
+            if (null == contents)
+                throw new ArgumentNullException("contents");
 
-			this.str = str;
+            m_contents = clone ? Arrays.Clone(contents) : contents;
         }
 
-		public override string GetString()
+        public override string GetString()
         {
-            return str;
+            return Strings.FromByteArray(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.T61String, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.T61String, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
         public byte[] GetOctets()
         {
-			return Strings.ToByteArray(str);
+            return Arrays.Clone(m_contents);
         }
 
-		protected override bool Asn1Equals(
-			Asn1Object asn1Object)
-		{
-			DerT61String other = asn1Object as DerT61String;
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            DerT61String that = asn1Object as DerT61String;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
 
-			if (other == null)
-				return false;
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
+        }
 
-            return this.str.Equals(other.str);
+        internal static DerT61String CreatePrimitive(byte[] contents)
+        {
+            return new DerT61String(contents, false);
         }
-	}
+    }
 }
diff --git a/crypto/src/asn1/DerUTCTime.cs b/crypto/src/asn1/DerUTCTime.cs
index eaa902e71..cb3f13353 100644
--- a/crypto/src/asn1/DerUTCTime.cs
+++ b/crypto/src/asn1/DerUTCTime.cs
@@ -1,6 +1,6 @@
 using System;
 using System.Globalization;
-using System.Text;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -12,47 +12,64 @@ namespace Org.BouncyCastle.Asn1
     public class DerUtcTime
         : Asn1Object
     {
-        private readonly string time;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerUtcTime), Asn1Tags.UtcTime) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
 		/**
-         * return an UTC Time from the passed in object.
+         * return a UTC Time from the passed in object.
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerUtcTime GetInstance(
-            object obj)
+        public static DerUtcTime GetInstance(object obj)
         {
             if (obj == null || obj is DerUtcTime)
             {
                 return (DerUtcTime)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerUtcTime)
+                    return (DerUtcTime)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerUtcTime)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct UTC time from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return an UTC Time from a tagged object.
+         * return a UTC Time from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerUtcTime GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerUtcTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
-
-			if (isExplicit || o is DerUtcTime)
-			{
-				return GetInstance(o);
-			}
-
-			return new DerUtcTime(((Asn1OctetString)o).GetOctets());
+            return (DerUtcTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
+        private readonly string time;
+
         /**
          * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
          * never encoded. When you're creating one of these objects from scratch, that's
@@ -62,8 +79,7 @@ namespace Org.BouncyCastle.Asn1
          * <p>
          * @param time the time string.</p>
          */
-        public DerUtcTime(
-            string time)
+        public DerUtcTime(string time)
         {
 			if (time == null)
 				throw new ArgumentNullException("time");
@@ -83,8 +99,7 @@ namespace Org.BouncyCastle.Asn1
 		/**
          * base constructor from a DateTime object
          */
-        public DerUtcTime(
-            DateTime time)
+        public DerUtcTime(DateTime time)
         {
 #if PORTABLE
             this.time = time.ToUniversalTime().ToString("yyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
@@ -93,13 +108,12 @@ namespace Org.BouncyCastle.Asn1
 #endif
         }
 
-		internal DerUtcTime(
-            byte[] bytes)
+		internal DerUtcTime(byte[] contents)
         {
             //
             // explicitly convert to characters
             //
-            this.time = Strings.FromAsciiByteArray(bytes);
+            this.time = Strings.FromAsciiByteArray(contents);
         }
 
 //		public DateTime ToDateTime()
@@ -139,9 +153,7 @@ namespace Org.BouncyCastle.Asn1
 			return ParseDateString(AdjustedTimeString, @"yyyyMMddHHmmss'GMT'zzz");
 		}
 
-		private DateTime ParseDateString(
-			string	dateStr,
-			string	formatStr)
+		private DateTime ParseDateString(string dateStr, string formatStr)
 		{
 			DateTime dt = DateTime.ParseExact(
 				dateStr,
@@ -247,15 +259,11 @@ namespace Org.BouncyCastle.Asn1
             return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
         }
 
-        protected override bool Asn1Equals(
-			Asn1Object asn1Object)
+        protected override bool Asn1Equals(Asn1Object asn1Object)
 		{
-			DerUtcTime other = asn1Object as DerUtcTime;
-
-			if (other == null)
-				return false;
-
-			return this.time.Equals(other.time);
+			DerUtcTime that = asn1Object as DerUtcTime;
+            return null != that
+                && this.time.Equals(that.time);
         }
 
 		protected override int Asn1GetHashCode()
@@ -267,5 +275,10 @@ namespace Org.BouncyCastle.Asn1
 		{
 			return time;
 		}
-	}
+
+        internal static DerUtcTime CreatePrimitive(byte[] contents)
+        {
+            return new DerUtcTime(contents);
+        }
+    }
 }
diff --git a/crypto/src/asn1/DerUTF8String.cs b/crypto/src/asn1/DerUTF8String.cs
index 24023cd6c..d15a19d39 100644
--- a/crypto/src/asn1/DerUTF8String.cs
+++ b/crypto/src/asn1/DerUTF8String.cs
@@ -1,5 +1,5 @@
 using System;
-using System.Text;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -11,92 +11,112 @@ namespace Org.BouncyCastle.Asn1
     public class DerUtf8String
         : DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerUtf8String), Asn1Tags.Utf8String) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
 		/**
          * return an UTF8 string from the passed in object.
          *
          * @exception ArgumentException if the object cannot be converted.
          */
-        public static DerUtf8String GetInstance(
-            object obj)
+        public static DerUtf8String GetInstance(object obj)
         {
             if (obj == null || obj is DerUtf8String)
             {
                 return (DerUtf8String)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerUtf8String)
+                    return (DerUtf8String)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerUtf8String)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct UTF8 string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return an UTF8 string from a tagged object.
+         * return a UTF8 string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerUtf8String GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerUtf8String GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
+            return (DerUtf8String)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
 
-			if (isExplicit || o is DerUtf8String)
-			{
-				return GetInstance(o);
-			}
+        private readonly byte[] m_contents;
 
-			return new DerUtf8String(Asn1OctetString.GetInstance(o).GetOctets());
+        public DerUtf8String(string str)
+            : this(Strings.ToUtf8ByteArray(str), false)
+        {
         }
 
-        /**
-         * basic constructor - byte encoded string.
-         */
-        public DerUtf8String(
-            byte[] str)
-			: this(Encoding.UTF8.GetString(str, 0, str.Length))
+        public DerUtf8String(byte[] contents)
+            : this(contents, true)
         {
         }
 
-		/**
-         * basic constructor
-         */
-        public DerUtf8String(
-            string str)
+        internal DerUtf8String(byte[] contents, bool clone)
         {
-			if (str == null)
-				throw new ArgumentNullException("str");
+            if (null == contents)
+                throw new ArgumentNullException("contents");
 
-			this.str = str;
+            m_contents = clone ? Arrays.Clone(contents) : contents;
         }
 
-		public override string GetString()
+        public override string GetString()
         {
-            return str;
+            return Strings.FromUtf8ByteArray(m_contents);
         }
 
-		protected override bool Asn1Equals(
-			Asn1Object asn1Object)
+		protected override bool Asn1Equals(Asn1Object asn1Object)
 		{
-			DerUtf8String other = asn1Object as DerUtf8String;
-
-			if (other == null)
-				return false;
+			DerUtf8String that = asn1Object as DerUtf8String;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
 
-			return this.str.Equals(other.str);
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Utf8String, Encoding.UTF8.GetBytes(str));
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.Utf8String, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, Encoding.UTF8.GetBytes(str));
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
+        }
+
+        internal static DerUtf8String CreatePrimitive(byte[] contents)
+        {
+            return new DerUtf8String(contents, false);
         }
     }
 }
diff --git a/crypto/src/asn1/DerUniversalString.cs b/crypto/src/asn1/DerUniversalString.cs
index 1ae989f93..e4e93bd7d 100644
--- a/crypto/src/asn1/DerUniversalString.cs
+++ b/crypto/src/asn1/DerUniversalString.cs
@@ -1,4 +1,6 @@
 using System;
+using System.Diagnostics;
+using System.IO;
 using System.Text;
 
 using Org.BouncyCastle.Utilities;
@@ -6,17 +8,27 @@ using Org.BouncyCastle.Utilities;
 namespace Org.BouncyCastle.Asn1
 {
     /**
-     * Der UniversalString object.
+     * UniversalString object.
      */
     public class DerUniversalString
         : DerStringBase
     {
-        private static readonly char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerUniversalString), Asn1Tags.UniversalString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
-		private readonly byte[] str;
+        private static readonly char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
 
 		/**
-         * return a Universal string from the passed in object.
+         * return a universal string from the passed in object.
          *
          * @exception ArgumentException if the object cannot be converted.
          */
@@ -27,85 +39,134 @@ namespace Org.BouncyCastle.Asn1
             {
                 return (DerUniversalString)obj;
             }
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerUniversalString)
+                    return (DerUniversalString)asn1Object;
+            }
+            else if (obj is byte[])
+            {
+                try
+                {
+                    return (DerUniversalString)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct universal string from byte[]: " + e.Message);
+                }
+            }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return a Universal string from a tagged object.
+         * return a universal string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerUniversalString GetInstance(
-            Asn1TaggedObject	obj,
-            bool				isExplicit)
+        public static DerUniversalString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
+            return (DerUniversalString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
 
-			if (isExplicit || o is DerUniversalString)
-			{
-				return GetInstance(o);
-			}
+        private readonly byte[] m_contents;
 
-			return new DerUniversalString(Asn1OctetString.GetInstance(o).GetOctets());
+        public DerUniversalString(byte[] contents)
+            : this(contents, true)
+        {
         }
 
-        /**
-         * basic constructor - byte encoded string.
-         */
-        public DerUniversalString(
-            byte[] str)
+        internal DerUniversalString(byte[] contents, bool clone)
         {
-			if (str == null)
-				throw new ArgumentNullException("str");
+            if (null == contents)
+                throw new ArgumentNullException("contents");
 
-			this.str = str;
+            m_contents = clone ? Arrays.Clone(contents) : contents;
         }
 
         public override string GetString()
         {
-			StringBuilder buffer = new StringBuilder("#");
-			byte[] enc = GetDerEncoded();
+            int dl = m_contents.Length;
+            int capacity = 3 + 2 * (Asn1OutputStream.GetLengthOfDL(dl) + dl);
+            StringBuilder buf = new StringBuilder("#1C", capacity);
+            EncodeHexDL(buf, dl);
 
-			for (int i = 0; i != enc.Length; i++)
-			{
-				uint ubyte = enc[i];
-				buffer.Append(table[(ubyte >> 4) & 0xf]);
-				buffer.Append(table[enc[i] & 0xf]);
-			}
+            for (int i = 0; i < dl; ++i)
+            {
+                EncodeHexByte(buf, m_contents[i]);
+            }
 
-            return buffer.ToString();
+            Debug.Assert(buf.Length == capacity);
+            return buf.ToString();
         }
 
-		public byte[] GetOctets()
+        public byte[] GetOctets()
         {
-            return (byte[]) str.Clone();
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UniversalString, this.str);
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UniversalString, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, this.str);
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
+        }
+
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            DerUniversalString that = asn1Object as DerUniversalString;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
+
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
+        }
+
+        internal static DerUniversalString CreatePrimitive(byte[] contents)
+        {
+            return new DerUniversalString(contents, false);
+        }
+
+        private static void EncodeHexByte(StringBuilder buf, int i)
+        {
+            buf.Append(table[(i >> 4) & 0xF]);
+            buf.Append(table[i & 0xF]);
         }
 
-        protected override bool Asn1Equals(
-			Asn1Object asn1Object)
-		{
-			DerUniversalString other = asn1Object as DerUniversalString;
+        private static void EncodeHexDL(StringBuilder buf, int dl)
+        {
+            if (dl < 128)
+            {
+                EncodeHexByte(buf, dl);
+                return;
+            }
+
+            byte[] stack = new byte[5];
+            int pos = 5;
 
-			if (other == null)
-				return false;
+            do
+            {
+                stack[--pos] = (byte)dl;
+                dl >>= 8;
+            }
+            while (dl != 0);
+
+            int count = stack.Length - pos;
+            stack[--pos] = (byte)(0x80 | count);
 
-//			return this.GetString().Equals(other.GetString());
-			return Arrays.AreEqual(this.str, other.str);
+            do
+            {
+                EncodeHexByte(buf, stack[pos++]);
+            }
+            while (pos < stack.Length);
         }
     }
 }
diff --git a/crypto/src/asn1/DerVideotexString.cs b/crypto/src/asn1/DerVideotexString.cs
index 3511663d2..a5fbe0602 100644
--- a/crypto/src/asn1/DerVideotexString.cs
+++ b/crypto/src/asn1/DerVideotexString.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
@@ -7,10 +8,20 @@ namespace Org.BouncyCastle.Asn1
     public class DerVideotexString
         : DerStringBase
     {
-        private readonly byte[] mString;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerVideotexString), Asn1Tags.VideotexString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
         /**
-         * return a Videotex String from the passed in object
+         * return a videotex string from the passed in object
          *
          * @param obj a DERVideotexString or an object that can be converted into one.
          * @exception IllegalArgumentException if the object cannot be converted.
@@ -22,16 +33,21 @@ namespace Org.BouncyCastle.Asn1
             {
                 return (DerVideotexString)obj;
             }
-
-            if (obj is byte[])
+            else if (obj is IAsn1Convertible)
+            {
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerVideotexString)
+                    return (DerVideotexString)asn1Object;
+            }
+            else if (obj is byte[])
             {
                 try
                 {
-                    return (DerVideotexString)FromByteArray((byte[])obj);
+                    return (DerVideotexString)Meta.Instance.FromByteArray((byte[])obj);
                 }
-                catch (Exception e)
+                catch (IOException e)
                 {
-                    throw new ArgumentException("encoding error in GetInstance: " + e.ToString(), "obj");
+                    throw new ArgumentException("failed to construct videotex string from byte[]: " + e.Message);
                 }
             }
 
@@ -39,70 +55,68 @@ namespace Org.BouncyCastle.Asn1
         }
 
         /**
-         * return a Videotex String from a tagged object.
+         * return a videotex string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicit true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception IllegalArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception IllegalArgumentException if the tagged object cannot be converted.
          * @return a DERVideotexString instance, or null.
          */
-        public static DerVideotexString GetInstance(Asn1TaggedObject obj, bool isExplicit)
+        public static DerVideotexString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-			Asn1Object o = obj.GetObject();
+            return (DerVideotexString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
 
-            if (isExplicit || o is DerVideotexString)
-			{
-				return GetInstance(o);
-			}
+        private readonly byte[] m_contents;
 
-            return new DerVideotexString(((Asn1OctetString)o).GetOctets());
+        public DerVideotexString(byte[] contents)
+            : this(contents, true)
+        {
         }
 
-        /**
-         * basic constructor - with bytes.
-         * @param string the byte encoding of the characters making up the string.
-         */
-        public DerVideotexString(byte[] encoding)
+        internal DerVideotexString(byte[] contents, bool clone)
         {
-            this.mString = Arrays.Clone(encoding);
+            if (null == contents)
+                throw new ArgumentNullException("contents");
+
+            m_contents = clone ? Arrays.Clone(contents) : contents;
         }
 
         public override string GetString()
         {
-            return Strings.FromByteArray(mString);
+            return Strings.FromByteArray(m_contents);
         }
 
         public byte[] GetOctets()
         {
-            return Arrays.Clone(mString);
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VideotexString, mString);
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VideotexString, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, mString);
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
-        protected override int Asn1GetHashCode()
-		{
-            return Arrays.GetHashCode(mString);
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            DerVideotexString that = asn1Object as DerVideotexString;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
         }
 
-		protected override bool Asn1Equals(
-            Asn1Object asn1Object)
+        protected override int Asn1GetHashCode()
         {
-            DerVideotexString other = asn1Object as DerVideotexString;
-
-            if (other == null)
-				return false;
+            return Arrays.GetHashCode(m_contents);
+        }
 
-            return Arrays.AreEqual(mString, other.mString);
+        internal static DerVideotexString CreatePrimitive(byte[] contents)
+        {
+            return new DerVideotexString(contents, false);
         }
     }
 }
diff --git a/crypto/src/asn1/DerVisibleString.cs b/crypto/src/asn1/DerVisibleString.cs
index cbbd9cc98..359370040 100644
--- a/crypto/src/asn1/DerVisibleString.cs
+++ b/crypto/src/asn1/DerVisibleString.cs
@@ -1,20 +1,30 @@
 using System;
-using System.Text;
+using System.IO;
 
 using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1
 {
     /**
-     * Der VisibleString object.
+     * VisibleString object.
      */
     public class DerVisibleString
         : DerStringBase
     {
-        private readonly string str;
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(DerVisibleString), Asn1Tags.VisibleString) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
 
         /**
-         * return a Visible string from the passed in object.
+         * return a visible string from the passed in object.
          *
          * @exception ArgumentException if the object cannot be converted.
          */
@@ -25,91 +35,97 @@ namespace Org.BouncyCastle.Asn1
             {
                 return (DerVisibleString)obj;
             }
-
-            if (obj is Asn1OctetString)
+            else if (obj is IAsn1Convertible)
             {
-                return new DerVisibleString(((Asn1OctetString)obj).GetOctets());
+                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
+                if (asn1Object is DerVisibleString)
+                    return (DerVisibleString)asn1Object;
             }
-
-            if (obj is Asn1TaggedObject)
+            else if (obj is byte[])
             {
-                return GetInstance(((Asn1TaggedObject)obj).GetObject());
+                try
+                {
+                    return (DerVisibleString)Meta.Instance.FromByteArray((byte[])obj);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct visible string from byte[]: " + e.Message);
+                }
             }
 
             throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
         }
 
         /**
-         * return a Visible string from a tagged object.
+         * return a visible string from a tagged object.
          *
-         * @param obj the tagged object holding the object we want
-         * @param explicitly true if the object is meant to be explicitly
-         *              tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot
-         *               be converted.
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
          */
-        public static DerVisibleString GetInstance(
-            Asn1TaggedObject	obj,
-            bool				explicitly)
+        public static DerVisibleString GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            return GetInstance(obj.GetObject());
+            return (DerVisibleString)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
         }
 
-        /**
-         * basic constructor - byte encoded string.
-         */
-        public DerVisibleString(
-            byte[] str)
-			: this(Strings.FromAsciiByteArray(str))
-        {
-        }
+        private readonly byte[] m_contents;
 
-		/**
-         * basic constructor
-         */
-        public DerVisibleString(
-            string str)
+        public DerVisibleString(string str)
         {
 			if (str == null)
 				throw new ArgumentNullException("str");
 
-			this.str = str;
+			m_contents = Strings.ToAsciiByteArray(str);
         }
 
-		public override string GetString()
+        public DerVisibleString(byte[] contents)
+            : this(contents, true)
         {
-            return str;
+        }
+
+        internal DerVisibleString(byte[] contents, bool clone)
+        {
+            if (null == contents)
+                throw new ArgumentNullException("contents");
+
+            m_contents = clone ? Arrays.Clone(contents) : contents;
+        }
+
+        public override string GetString()
+        {
+            return Strings.FromAsciiByteArray(m_contents);
         }
 
 		public byte[] GetOctets()
         {
-            return Strings.ToAsciiByteArray(str);
+            return Arrays.Clone(m_contents);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VisibleString, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.VisibleString, m_contents);
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
         }
 
-        protected override bool Asn1Equals(
-			Asn1Object asn1Object)
-		{
-			DerVisibleString other = asn1Object as DerVisibleString;
-
-			if (other == null)
-				return false;
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            DerVisibleString that = asn1Object as DerVisibleString;
+            return null != that
+                && Arrays.AreEqual(this.m_contents, that.m_contents);
+        }
 
-			return this.str.Equals(other.str);
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
         }
 
-		protected override int Asn1GetHashCode()
-		{
-            return this.str.GetHashCode();
+        internal static DerVisibleString CreatePrimitive(byte[] contents)
+        {
+            return new DerVisibleString(contents, false);
         }
     }
 }
diff --git a/crypto/src/asn1/ocsp/CertStatus.cs b/crypto/src/asn1/ocsp/CertStatus.cs
index 7dd99b844..8a4d2242d 100644
--- a/crypto/src/asn1/ocsp/CertStatus.cs
+++ b/crypto/src/asn1/ocsp/CertStatus.cs
@@ -34,22 +34,23 @@ namespace Org.BouncyCastle.Asn1.Ocsp
             this.value = value;
         }
 
-		public CertStatus(
-            Asn1TaggedObject choice)
+		public CertStatus(Asn1TaggedObject choice)
         {
             this.tagNo = choice.TagNo;
 
 			switch (choice.TagNo)
             {
-				case 1:
-					value = RevokedInfo.GetInstance(choice, false);
-					break;
-				case 0:
-				case 2:
-					value = DerNull.Instance;
-					break;
-				default:
-					throw new ArgumentException("Unknown tag encountered: " + choice.TagNo);
+            case 0:
+                value = Asn1Null.GetInstance(choice, false);
+                break;
+            case 1:
+				value = RevokedInfo.GetInstance(choice, false);
+				break;
+			case 2:
+                value = Asn1Null.GetInstance(choice, false);
+                break;
+			default:
+				throw new ArgumentException("Unknown tag encountered: " + Asn1Utilities.GetTagText(choice));
             }
         }