summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crypto/BouncyCastle.Android.csproj2
-rw-r--r--crypto/BouncyCastle.csproj2
-rw-r--r--crypto/BouncyCastle.iOS.csproj2
-rw-r--r--crypto/crypto.csproj10
-rw-r--r--crypto/src/asn1/ASN1StreamParser.cs4
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs6
-rw-r--r--crypto/src/asn1/Asn1OutputStream.cs5
-rw-r--r--crypto/src/asn1/Asn1Sequence.cs49
-rw-r--r--crypto/src/asn1/Asn1Set.cs129
-rw-r--r--crypto/src/asn1/BerSequence.cs20
-rw-r--r--crypto/src/asn1/BerSet.cs25
-rw-r--r--crypto/src/asn1/DERSequenceParser.cs16
-rw-r--r--crypto/src/asn1/DERSetParser.cs14
-rw-r--r--crypto/src/asn1/DLSequence.cs96
-rw-r--r--crypto/src/asn1/DLSet.cs91
-rw-r--r--crypto/src/asn1/DerOutputStream.cs5
-rw-r--r--crypto/src/asn1/DerSequence.cs8
-rw-r--r--crypto/src/asn1/DerSet.cs43
-rw-r--r--crypto/src/asn1/LazyDLSequence.cs2
-rw-r--r--crypto/src/asn1/LazyDLSet.cs3
20 files changed, 386 insertions, 146 deletions
diff --git a/crypto/BouncyCastle.Android.csproj b/crypto/BouncyCastle.Android.csproj
index 567351082..8d7900444 100644
--- a/crypto/BouncyCastle.Android.csproj
+++ b/crypto/BouncyCastle.Android.csproj
@@ -132,6 +132,8 @@
     <Compile Include="src\asn1\DerUniversalString.cs" />
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
+    <Compile Include="src\asn1\DLSequence.cs" />
+    <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\IAsn1ApplicationSpecificParser.cs" />
     <Compile Include="src\asn1\IAsn1Choice.cs" />
     <Compile Include="src\asn1\IAsn1Convertible.cs" />
diff --git a/crypto/BouncyCastle.csproj b/crypto/BouncyCastle.csproj
index aec91b946..495b55220 100644
--- a/crypto/BouncyCastle.csproj
+++ b/crypto/BouncyCastle.csproj
@@ -126,6 +126,8 @@
     <Compile Include="src\asn1\DerUniversalString.cs" />
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
+    <Compile Include="src\asn1\DLSequence.cs" />
+    <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\IAsn1ApplicationSpecificParser.cs" />
     <Compile Include="src\asn1\IAsn1Choice.cs" />
     <Compile Include="src\asn1\IAsn1Convertible.cs" />
diff --git a/crypto/BouncyCastle.iOS.csproj b/crypto/BouncyCastle.iOS.csproj
index 83d8c66a2..9333ba1e5 100644
--- a/crypto/BouncyCastle.iOS.csproj
+++ b/crypto/BouncyCastle.iOS.csproj
@@ -127,6 +127,8 @@
     <Compile Include="src\asn1\DerUniversalString.cs" />
     <Compile Include="src\asn1\DerVideotexString.cs" />
     <Compile Include="src\asn1\DerVisibleString.cs" />
+    <Compile Include="src\asn1\DLSequence.cs" />
+    <Compile Include="src\asn1\DLSet.cs" />
     <Compile Include="src\asn1\IAsn1ApplicationSpecificParser.cs" />
     <Compile Include="src\asn1\IAsn1Choice.cs" />
     <Compile Include="src\asn1\IAsn1Convertible.cs" />
diff --git a/crypto/crypto.csproj b/crypto/crypto.csproj
index 0f90a842d..8c5f70e30 100644
--- a/crypto/crypto.csproj
+++ b/crypto/crypto.csproj
@@ -519,6 +519,16 @@
                     BuildAction = "Compile"
                 />
                 <File
+                    RelPath = "src\asn1\DLSequence.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
+                    RelPath = "src\asn1\DLSet.cs"
+                    SubType = "Code"
+                    BuildAction = "Compile"
+                />
+                <File
                     RelPath = "src\asn1\IAsn1ApplicationSpecificParser.cs"
                     SubType = "Code"
                     BuildAction = "Compile"
diff --git a/crypto/src/asn1/ASN1StreamParser.cs b/crypto/src/asn1/ASN1StreamParser.cs
index a92a3adae..0a575c8c5 100644
--- a/crypto/src/asn1/ASN1StreamParser.cs
+++ b/crypto/src/asn1/ASN1StreamParser.cs
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Asn1
 
 			return v.Count == 1
 				?   new DerTaggedObject(true, tag, v[0])
-				:   new DerTaggedObject(false, tag, DerSequence.FromVector(v));
+				:   new DerTaggedObject(false, tag, DLSequence.FromVector(v));
 		}
 
 		public virtual IAsn1Convertible ReadObject()
@@ -122,7 +122,7 @@ namespace Org.BouncyCastle.Asn1
 			if (tag == -1)
 				return null;
 
-			// turn of looking for "00" while we resolve the tag
+			// turn off looking for "00" while we resolve the tag
 			Set00Check(false);
 
 			//
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 14073e921..27c583338 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -113,7 +113,7 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.Set:
                 return CreateDLSet(defIn);
             case Asn1Tags.External:
-                return DerSequence.FromVector(ReadVector(defIn)).ToAsn1External();
+                return DLSequence.FromVector(ReadVector(defIn)).ToAsn1External();
             default:
                 throw new IOException("unknown tag " + tagNo + " encountered");
             }
@@ -145,12 +145,12 @@ namespace Org.BouncyCastle.Asn1
 
         internal virtual Asn1Sequence CreateDLSequence(DefiniteLengthInputStream defIn)
         {
-            return DerSequence.FromVector(ReadVector(defIn));
+            return DLSequence.FromVector(ReadVector(defIn));
         }
 
         internal virtual Asn1Set CreateDLSet(DefiniteLengthInputStream defIn)
         {
-            return DerSet.FromVector(ReadVector(defIn), false);
+            return DLSet.FromVector(ReadVector(defIn));
         }
 
         public Asn1Object ReadObject()
diff --git a/crypto/src/asn1/Asn1OutputStream.cs b/crypto/src/asn1/Asn1OutputStream.cs
index f1e9aec57..b0d4a18fa 100644
--- a/crypto/src/asn1/Asn1OutputStream.cs
+++ b/crypto/src/asn1/Asn1OutputStream.cs
@@ -57,6 +57,11 @@ namespace Org.BouncyCastle.Asn1
             get { return true; }
         }
 
+        internal virtual bool IsDer
+        {
+            get { return false; }
+        }
+
         internal void WriteDL(int length)
         {
             if (length < 128)
diff --git a/crypto/src/asn1/Asn1Sequence.cs b/crypto/src/asn1/Asn1Sequence.cs
index e1d0fa675..8025492d2 100644
--- a/crypto/src/asn1/Asn1Sequence.cs
+++ b/crypto/src/asn1/Asn1Sequence.cs
@@ -10,9 +10,6 @@ namespace Org.BouncyCastle.Asn1
     public abstract class Asn1Sequence
         : Asn1Object, IEnumerable
     {
-        // NOTE: Only non-readonly to support LazyDerSequence
-        internal Asn1Encodable[] elements;
-
         /**
          * return an Asn1Sequence from the given object.
          *
@@ -57,49 +54,43 @@ namespace Org.BouncyCastle.Asn1
          * dealing with implicitly tagged sequences you really <b>should</b>
          * be using this method.
          *
-         * @param obj the tagged object.
-         * @param explicitly true if the object is meant to be explicitly tagged,
+         * @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.
          */
-        public static Asn1Sequence GetInstance(
-            Asn1TaggedObject	obj,
-            bool				explicitly)
+        public static Asn1Sequence GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object inner = obj.GetObject();
+            Asn1Object baseObject = taggedObject.GetObject();
 
-            if (explicitly)
+            if (declaredExplicit)
             {
-                if (!obj.IsExplicit())
+                if (!taggedObject.IsExplicit())
                     throw new ArgumentException("object implicit - explicit expected.");
 
-                return (Asn1Sequence) inner;
+                return (Asn1Sequence)baseObject;
             }
 
-            //
-            // constructed object which appears to be explicitly tagged
-            // when it should be implicit means we have to add the
-            // surrounding sequence.
-            //
-            if (obj.IsExplicit())
+            // If parsed as explicit though declared implicit, it should have been a sequence of one
+            if (taggedObject.IsExplicit())
             {
-                if (obj is BerTaggedObject)
-                {
-                    return new BerSequence(inner);
-                }
+                if (taggedObject is BerTaggedObject)
+                    return new BerSequence(baseObject);
 
-                return new DerSequence(inner);
+                return new DLSequence(baseObject);
             }
 
-            if (inner is Asn1Sequence)
-            {
-                return (Asn1Sequence) inner;
-            }
+            if (baseObject is Asn1Sequence)
+                return (Asn1Sequence)baseObject;
 
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject),
+                "taggedObject");
         }
 
+        // NOTE: Only non-readonly to support LazyDLSequence
+        internal Asn1Encodable[] elements;
+
         protected internal Asn1Sequence()
         {
             this.elements = Asn1EncodableVector.EmptyElements;
@@ -286,5 +277,7 @@ namespace Org.BouncyCastle.Asn1
         internal abstract DerExternal ToAsn1External();
 
         internal abstract Asn1OctetString ToAsn1OctetString();
+
+        internal abstract Asn1Set ToAsn1Set();
     }
 }
diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index 453627b7c..b1439be47 100644
--- a/crypto/src/asn1/Asn1Set.cs
+++ b/crypto/src/asn1/Asn1Set.cs
@@ -13,12 +13,9 @@ using Org.BouncyCastle.Utilities.Collections;
 
 namespace Org.BouncyCastle.Asn1
 {
-    abstract public class Asn1Set
+    public abstract class Asn1Set
         : Asn1Object, IEnumerable
     {
-        // NOTE: Only non-readonly to support LazyDerSet
-        internal Asn1Encodable[] elements;
-
         /**
          * return an ASN1Set from the given object.
          *
@@ -63,65 +60,52 @@ namespace Org.BouncyCastle.Asn1
          * dealing with implicitly tagged sets you really <b>should</b>
          * be using this method.
          *
-         * @param obj the tagged object.
-         * @param explicitly true if the object is meant to be explicitly tagged
+         * @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.
          */
-        public static Asn1Set GetInstance(
-            Asn1TaggedObject	obj,
-            bool				explicitly)
+        public static Asn1Set GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            Asn1Object inner = obj.GetObject();
+            Asn1Object baseObject = taggedObject.GetObject();
 
-            if (explicitly)
+            if (declaredExplicit)
             {
-                if (!obj.IsExplicit())
+                if (!taggedObject.IsExplicit())
                     throw new ArgumentException("object implicit - explicit expected.");
 
-                return (Asn1Set) inner;
+                return (Asn1Set)baseObject;
             }
 
-            //
-            // constructed object which appears to be explicitly tagged
-            // and it's really implicit means we have to add the
-            // surrounding sequence.
-            //
-            if (obj.IsExplicit())
+            // If parsed as explicit though declared implicit, it should have been a set of one
+            if (taggedObject.IsExplicit())
             {
-                return new DerSet(inner);
-            }
+                if (taggedObject is BerTaggedObject)
+                    return new BerSet(baseObject);
 
-            if (inner is Asn1Set)
-            {
-                return (Asn1Set) inner;
+                return new DLSet(baseObject);
             }
 
-            //
-            // in this case the parser returns a sequence, convert it
-            // into a set.
-            //
-            if (inner is Asn1Sequence)
-            {
-                Asn1EncodableVector v = new Asn1EncodableVector();
-                Asn1Sequence s = (Asn1Sequence) inner;
+            if (baseObject is Asn1Set)
+                return (Asn1Set)baseObject;
 
-                foreach (Asn1Encodable ae in s)
-                {
-                    v.Add(ae);
-                }
-
-                // TODO Should be able to construct set directly from sequence?
-                return new DerSet(v, false);
-            }
+            // Parser assumes implicit constructed encodings are sequences
+            if (baseObject is Asn1Sequence)
+                return ((Asn1Sequence)baseObject).ToAsn1Set();
 
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(taggedObject),
+                "taggedObject");
         }
 
+        // NOTE: Only non-readonly to support LazyDLSet
+        internal Asn1Encodable[] elements;
+        internal bool isSorted;
+
         protected internal Asn1Set()
         {
             this.elements = Asn1EncodableVector.EmptyElements;
+            this.isSorted = true;
         }
 
         protected internal Asn1Set(Asn1Encodable element)
@@ -130,22 +114,47 @@ namespace Org.BouncyCastle.Asn1
                 throw new ArgumentNullException("element");
 
             this.elements = new Asn1Encodable[]{ element };
+            this.isSorted = true;
         }
 
-        protected internal Asn1Set(params Asn1Encodable[] elements)
+        protected internal Asn1Set(Asn1Encodable[] elements, bool doSort)
         {
             if (Arrays.IsNullOrContainsNull(elements))
                 throw new NullReferenceException("'elements' cannot be null, or contain null");
 
-            this.elements = Asn1EncodableVector.CloneElements(elements);
+            Asn1Encodable[] tmp = Asn1EncodableVector.CloneElements(elements);
+            if (doSort && tmp.Length >= 2)
+            {
+                tmp = Sort(tmp);
+            }
+
+            this.elements = tmp;
+            this.isSorted = doSort || tmp.Length < 2;
         }
 
-        protected internal Asn1Set(Asn1EncodableVector elementVector)
+        protected internal Asn1Set(Asn1EncodableVector elementVector, bool doSort)
         {
             if (null == elementVector)
                 throw new ArgumentNullException("elementVector");
 
-            this.elements = elementVector.TakeElements();
+            Asn1Encodable[] tmp;
+            if (doSort && elementVector.Count >= 2)
+            {
+                tmp = Sort(elementVector.CopyElements());
+            }
+            else
+            {
+                tmp = elementVector.TakeElements();
+            }
+
+            this.elements = tmp;
+            this.isSorted = doSort || tmp.Length < 2;
+        }
+
+        protected internal Asn1Set(bool isSorted, Asn1Encodable[] elements)
+        {
+            this.elements = elements;
+            this.isSorted = isSorted || elements.Length < 2;
         }
 
         public virtual IEnumerator GetEnumerator()
@@ -257,15 +266,24 @@ namespace Org.BouncyCastle.Asn1
             return true;
         }
 
-        protected internal void Sort()
+        internal override bool EncodeConstructed()
         {
-            // NOTE: Call Count here to 'force' a LazyDerSet
-            int count = Count;
+            return true;
+        }
+
+        public override string ToString()
+        {
+            return CollectionUtilities.ToString(elements);
+        }
+
+        private static Asn1Encodable[] Sort(Asn1Encodable[] elements)
+        {
+            int count = elements.Length;
             if (count < 2)
-                return;
+                return elements;
 
 #if PORTABLE
-            this.elements = elements
+            return elements
                 .Cast<Asn1Encodable>()
                 .Select(a => new { Item = a, Key = a.GetEncoded(Asn1Encodable.Der) })
                 .OrderBy(t => t.Key, new DerComparer())
@@ -278,19 +296,10 @@ namespace Org.BouncyCastle.Asn1
                 keys[i] = elements[i].GetEncoded(Der);
             }
             Array.Sort(keys, elements, new DerComparer());
+            return elements;
 #endif
         }
 
-        internal override bool EncodeConstructed()
-        {
-            return true;
-        }
-
-        public override string ToString()
-        {
-            return CollectionUtilities.ToString(elements);
-        }
-
 #if PORTABLE
         private class DerComparer
             : IComparer<byte[]>
diff --git a/crypto/src/asn1/BerSequence.cs b/crypto/src/asn1/BerSequence.cs
index e0525efa6..c8c5bbbbd 100644
--- a/crypto/src/asn1/BerSequence.cs
+++ b/crypto/src/asn1/BerSequence.cs
@@ -55,14 +55,13 @@ namespace Org.BouncyCastle.Asn1
 
         internal override void Encode(Asn1OutputStream asn1Out, bool withID)
 		{
-			if (asn1Out.IsBer)
+			if (!asn1Out.IsBer)
             {
-                asn1Out.WriteEncodingIL(withID, Asn1Tags.Constructed | Asn1Tags.Sequence, elements);
-			}
-			else
-			{
-				base.Encode(asn1Out, withID);
-			}
+                base.Encode(asn1Out, withID);
+                return;
+            }
+
+            asn1Out.WriteEncodingIL(withID, Asn1Tags.Constructed | Asn1Tags.Sequence, elements);
 		}
 
         internal override DerBitString ToAsn1BitString()
@@ -74,12 +73,17 @@ namespace Org.BouncyCastle.Asn1
         {
             // TODO There is currently no BerExternal class (or ToDLObject/ToDerObject)
             //return ((Asn1Sequence)ToDLObject()).ToAsn1External();
-            return new DerSequence(elements, false).ToAsn1External();
+            return new DLSequence(elements).ToAsn1External();
         }
 
         internal override Asn1OctetString ToAsn1OctetString()
         {
             return new BerOctetString(GetConstructedOctetStrings());
         }
+
+        internal override Asn1Set ToAsn1Set()
+        {
+            return new BerSet(false, elements);
+        }
     }
 }
diff --git a/crypto/src/asn1/BerSet.cs b/crypto/src/asn1/BerSet.cs
index 5d61db6aa..2cfda2f09 100644
--- a/crypto/src/asn1/BerSet.cs
+++ b/crypto/src/asn1/BerSet.cs
@@ -14,13 +14,8 @@ namespace Org.BouncyCastle.Asn1
             return elementVector.Count < 1 ? Empty : new BerSet(elementVector);
 		}
 
-        internal static new BerSet FromVector(Asn1EncodableVector elementVector, bool needsSorting)
-		{
-            return elementVector.Count < 1 ? Empty : new BerSet(elementVector, needsSorting);
-		}
-
 		/**
-         * create an empty sequence
+         * create an empty set
          */
         public BerSet()
             : base()
@@ -35,6 +30,11 @@ namespace Org.BouncyCastle.Asn1
         {
         }
 
+        public BerSet(params Asn1Encodable[] elements)
+            : base(elements, false)
+        {
+        }
+
         /**
          * create a set containing a vector of objects.
          */
@@ -43,8 +43,8 @@ namespace Org.BouncyCastle.Asn1
         {
         }
 
-        internal BerSet(Asn1EncodableVector elementVector, bool needsSorting)
-            : base(elementVector, needsSorting)
+        internal BerSet(bool isSorted, Asn1Encodable[] elements)
+            : base(isSorted, elements)
         {
         }
 
@@ -55,14 +55,13 @@ namespace Org.BouncyCastle.Asn1
 
         internal override void Encode(Asn1OutputStream asn1Out, bool withID)
         {
-            if (asn1Out.IsBer)
-            {
-                asn1Out.WriteEncodingIL(withID, Asn1Tags.Constructed | Asn1Tags.Set, elements);
-            }
-            else
+            if (!asn1Out.IsBer)
             {
                 base.Encode(asn1Out, withID);
+                return;
             }
+
+            asn1Out.WriteEncodingIL(withID, Asn1Tags.Constructed | Asn1Tags.Set, elements);
         }
     }
 }
diff --git a/crypto/src/asn1/DERSequenceParser.cs b/crypto/src/asn1/DERSequenceParser.cs
index 69c2b9b2d..a373fcf32 100644
--- a/crypto/src/asn1/DERSequenceParser.cs
+++ b/crypto/src/asn1/DERSequenceParser.cs
@@ -1,24 +1,26 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1
 {
-	public class DerSequenceParser
+    // TODO[asn1] Should be renamed/replaced with DLSequenceParser
+    public class DerSequenceParser
 		: Asn1SequenceParser
 	{
-		private readonly Asn1StreamParser _parser;
+		private readonly Asn1StreamParser m_parser;
 
-		internal DerSequenceParser(
-			Asn1StreamParser parser)
+		internal DerSequenceParser(Asn1StreamParser parser)
 		{
-			this._parser = parser;
+			this.m_parser = parser;
 		}
 
 		public IAsn1Convertible ReadObject()
 		{
-			return _parser.ReadObject();
+			return m_parser.ReadObject();
 		}
 
 		public Asn1Object ToAsn1Object()
 		{
-			return new DerSequence(_parser.ReadVector());
+            return DLSequence.FromVector(m_parser.ReadVector());
 		}
 	}
 }
diff --git a/crypto/src/asn1/DERSetParser.cs b/crypto/src/asn1/DERSetParser.cs
index d67f135be..344de3bc1 100644
--- a/crypto/src/asn1/DERSetParser.cs
+++ b/crypto/src/asn1/DERSetParser.cs
@@ -1,24 +1,26 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1
 {
+    // TODO[asn1] Should be renamed/replaced with DLSetParser
 	public class DerSetParser
 		: Asn1SetParser
 	{
-		private readonly Asn1StreamParser _parser;
+		private readonly Asn1StreamParser m_parser;
 
-		internal DerSetParser(
-			Asn1StreamParser parser)
+		internal DerSetParser(Asn1StreamParser parser)
 		{
-			this._parser = parser;
+			this.m_parser = parser;
 		}
 
 		public IAsn1Convertible ReadObject()
 		{
-			return _parser.ReadObject();
+			return m_parser.ReadObject();
 		}
 
 		public Asn1Object ToAsn1Object()
 		{
-			return new DerSet(_parser.ReadVector(), false);
+			return DLSet.FromVector(m_parser.ReadVector());
 		}
 	}
 }
diff --git a/crypto/src/asn1/DLSequence.cs b/crypto/src/asn1/DLSequence.cs
new file mode 100644
index 000000000..56a0e6932
--- /dev/null
+++ b/crypto/src/asn1/DLSequence.cs
@@ -0,0 +1,96 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal class DLSequence
+        : DerSequence
+    {
+        internal static new readonly DLSequence Empty = new DLSequence();
+
+        internal static new DLSequence FromVector(Asn1EncodableVector elementVector)
+        {
+            return elementVector.Count < 1 ? Empty : new DLSequence(elementVector);
+        }
+
+        /**
+		 * create an empty sequence
+		 */
+        internal DLSequence()
+            : base()
+        {
+        }
+
+        /**
+		 * create a sequence containing one object
+		 */
+        internal DLSequence(Asn1Encodable element)
+            : base(element)
+        {
+        }
+
+        internal DLSequence(params Asn1Encodable[] elements)
+            : base(elements)
+        {
+        }
+
+        /**
+		 * create a sequence containing a vector of objects.
+		 */
+        internal DLSequence(Asn1EncodableVector elementVector)
+            : base(elementVector)
+        {
+        }
+
+        internal DLSequence(Asn1Encodable[] elements, bool clone)
+            : base(elements, clone)
+        {
+        }
+
+        internal override int EncodedLength(bool withID)
+        {
+            throw Platform.CreateNotImplementedException("DLSequence.EncodedLength");
+        }
+
+        internal override void Encode(Asn1OutputStream asn1Out, bool withID)
+        {
+            if (asn1Out.IsDer)
+            {
+                base.Encode(asn1Out, withID);
+                return;
+            }
+
+            if (Count < 1)
+            {
+                asn1Out.WriteEncodingDL(withID, Asn1Tags.Constructed | Asn1Tags.Sequence, Asn1OctetString.EmptyOctets);
+                return;
+            }
+
+            // TODO Intermediate buffer could be avoided if we could calculate expected length
+            MemoryStream bOut = new MemoryStream();
+            // TODO Once DLOutputStream exists, this should create one
+            Asn1OutputStream dOut = Asn1OutputStream.Create(bOut);
+            dOut.WriteElements(elements);
+            dOut.FlushInternal();
+
+#if PORTABLE
+            byte[] bytes = bOut.ToArray();
+            int length = bytes.Length;
+#else
+            byte[] bytes = bOut.GetBuffer();
+            int length = (int)bOut.Position;
+#endif
+
+            asn1Out.WriteEncodingDL(withID, Asn1Tags.Constructed | Asn1Tags.Sequence, bytes, 0, length);
+
+            Platform.Dispose(dOut);
+        }
+
+        internal override Asn1Set ToAsn1Set()
+        {
+            return new DLSet(false, elements);
+        }
+    }
+}
diff --git a/crypto/src/asn1/DLSet.cs b/crypto/src/asn1/DLSet.cs
new file mode 100644
index 000000000..0605a0167
--- /dev/null
+++ b/crypto/src/asn1/DLSet.cs
@@ -0,0 +1,91 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+    internal class DLSet
+        : DerSet
+    {
+        internal static new readonly DLSet Empty = new DLSet();
+
+        internal static new DLSet FromVector(Asn1EncodableVector elementVector)
+        {
+            return elementVector.Count < 1 ? Empty : new DLSet(elementVector);
+        }
+
+        /**
+         * create an empty set
+         */
+        internal DLSet()
+            : base()
+        {
+        }
+
+        /**
+         * create a set containing one object
+         */
+        internal DLSet(Asn1Encodable element)
+            : base(element)
+        {
+        }
+
+        internal DLSet(params Asn1Encodable[] elements)
+            : base(elements, false)
+        {
+        }
+
+        /**
+         * create a set containing a vector of objects.
+         */
+        internal DLSet(Asn1EncodableVector elementVector)
+            : base(elementVector, false)
+        {
+        }
+
+        internal DLSet(bool isSorted, Asn1Encodable[] elements)
+            : base(isSorted, elements)
+        {
+        }
+
+        internal override int EncodedLength(bool withID)
+        {
+            throw Platform.CreateNotImplementedException("DLSet.EncodedLength");
+        }
+
+        internal override void Encode(Asn1OutputStream asn1Out, bool withID)
+        {
+            if (asn1Out.IsDer)
+            {
+                base.Encode(asn1Out, withID);
+                return;
+            }
+
+            if (Count < 1)
+            {
+                asn1Out.WriteEncodingDL(withID, Asn1Tags.Constructed | Asn1Tags.Set, Asn1OctetString.EmptyOctets);
+                return;
+            }
+
+            // TODO Intermediate buffer could be avoided if we could calculate expected length
+            MemoryStream bOut = new MemoryStream();
+            // TODO Once DLOutputStream exists, this should create one
+            Asn1OutputStream dOut = Asn1OutputStream.Create(bOut);
+            dOut.WriteElements(elements);
+            dOut.FlushInternal();
+
+#if PORTABLE
+            byte[] bytes = bOut.ToArray();
+            int length = bytes.Length;
+#else
+            byte[] bytes = bOut.GetBuffer();
+            int length = (int)bOut.Position;
+#endif
+
+            asn1Out.WriteEncodingDL(withID, Asn1Tags.Constructed | Asn1Tags.Set, bytes, 0, length);
+
+            Platform.Dispose(dOut);
+        }
+    }
+}
diff --git a/crypto/src/asn1/DerOutputStream.cs b/crypto/src/asn1/DerOutputStream.cs
index b8cbe9ee2..f384c20a8 100644
--- a/crypto/src/asn1/DerOutputStream.cs
+++ b/crypto/src/asn1/DerOutputStream.cs
@@ -39,6 +39,11 @@ namespace Org.BouncyCastle.Asn1
             get { return false; }
         }
 
+        internal override bool IsDer
+        {
+            get { return true; }
+        }
+
         internal override void WritePrimitive(Asn1Object primitive, bool withID)
         {
             Asn1Set asn1Set = primitive as Asn1Set;
diff --git a/crypto/src/asn1/DerSequence.cs b/crypto/src/asn1/DerSequence.cs
index 5b315a909..157d99c93 100644
--- a/crypto/src/asn1/DerSequence.cs
+++ b/crypto/src/asn1/DerSequence.cs
@@ -74,7 +74,7 @@ namespace Org.BouncyCastle.Asn1
             MemoryStream bOut = new MemoryStream();
             Asn1OutputStream dOut = Asn1OutputStream.Create(bOut, Der);
             dOut.WriteElements(elements);
-            dOut.Flush();
+            dOut.FlushInternal();
 
 #if PORTABLE
             byte[] bytes = bOut.ToArray();
@@ -103,5 +103,11 @@ namespace Org.BouncyCastle.Asn1
         {
             return new DerOctetString(BerOctetString.FlattenOctetStrings(GetConstructedOctetStrings()));
         }
+
+        internal override Asn1Set ToAsn1Set()
+        {
+            // NOTE: DLSet is intentional, we don't want sorting
+            return new DLSet(false, elements);
+        }
     }
 }
diff --git a/crypto/src/asn1/DerSet.cs b/crypto/src/asn1/DerSet.cs
index d401d3a36..030b0a38f 100644
--- a/crypto/src/asn1/DerSet.cs
+++ b/crypto/src/asn1/DerSet.cs
@@ -18,11 +18,6 @@ namespace Org.BouncyCastle.Asn1
             return elementVector.Count < 1 ? Empty : new DerSet(elementVector);
 		}
 
-		internal static DerSet FromVector(Asn1EncodableVector elementVector, bool needsSorting)
-		{
-            return elementVector.Count < 1 ? Empty : new DerSet(elementVector, needsSorting);
-		}
-
 		/**
 		 * create an empty set
 		 */
@@ -39,29 +34,34 @@ namespace Org.BouncyCastle.Asn1
 		{
 		}
 
-		public DerSet(params Asn1Encodable[] elements)
-			: base(elements)
+        public DerSet(params Asn1Encodable[] elements)
+            : base(elements, true)
+        {
+        }
+
+        internal DerSet(Asn1Encodable[] elements, bool doSort)
+			: base(elements, doSort)
 		{
-			Sort();
 		}
 
 		/**
 		 * @param v - a vector of objects making up the set.
 		 */
 		public DerSet(Asn1EncodableVector elementVector)
-			: this(elementVector, true)
+			: base(elementVector, true)
 		{
 		}
 
-		internal DerSet(Asn1EncodableVector	elementVector, bool needsSorting)
-			: base(elementVector)
+		internal DerSet(Asn1EncodableVector	elementVector, bool doSort)
+			: base(elementVector, doSort)
 		{
-			if (needsSorting)
-			{
-				Sort();
-			}
 		}
 
+        internal DerSet(bool isSorted, Asn1Encodable[] elements)
+            : base(isSorted, elements)
+        {
+        }
+
         internal override int EncodedLength(bool withID)
         {
             throw Platform.CreateNotImplementedException("DerSet.EncodedLength");
@@ -83,11 +83,22 @@ namespace Org.BouncyCastle.Asn1
                 return;
             }
 
+            if (!isSorted)
+            {
+                new DerSet(elements, true).ImplEncode(asn1Out, withID);
+                return;
+            }
+
+            ImplEncode(asn1Out, withID);
+        }
+
+        private void ImplEncode(Asn1OutputStream asn1Out, bool withID)
+        {
             // TODO Intermediate buffer could be avoided if we could calculate expected length
             MemoryStream bOut = new MemoryStream();
             Asn1OutputStream dOut = Asn1OutputStream.Create(bOut, Der);
             dOut.WriteElements(elements);
-            dOut.Flush();
+            dOut.FlushInternal();
 
 #if PORTABLE
             byte[] bytes = bOut.ToArray();
diff --git a/crypto/src/asn1/LazyDLSequence.cs b/crypto/src/asn1/LazyDLSequence.cs
index 63152e446..fe924365e 100644
--- a/crypto/src/asn1/LazyDLSequence.cs
+++ b/crypto/src/asn1/LazyDLSequence.cs
@@ -5,7 +5,7 @@ using System.IO;
 namespace Org.BouncyCastle.Asn1
 {
     internal class LazyDLSequence
-        : DerSequence
+        : DLSequence
     {
         private byte[] encoded;
 
diff --git a/crypto/src/asn1/LazyDLSet.cs b/crypto/src/asn1/LazyDLSet.cs
index 0d4d84310..bdee0bc22 100644
--- a/crypto/src/asn1/LazyDLSet.cs
+++ b/crypto/src/asn1/LazyDLSet.cs
@@ -5,7 +5,7 @@ using System.IO;
 namespace Org.BouncyCastle.Asn1
 {
     internal class LazyDLSet
-        : DerSet
+        : DLSet
     {
         private byte[] encoded;
 
@@ -106,6 +106,7 @@ namespace Org.BouncyCastle.Asn1
                         Asn1EncodableVector v = input.ReadVector();
 
                         this.elements = v.TakeElements();
+                        this.isSorted = elements.Length < 2;
                         this.encoded = null;
                     }
                     catch (IOException e)