summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2020-02-19 21:03:22 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2020-02-19 21:03:22 +0700
commitad0b6c99d34da50d5473a9c14837a9ce199d0200 (patch)
treefbe5e81540f15b7a3cca0401380d33bd9fbb9cc6
parentCase-insensitive check of boolean env. var. (diff)
downloadBouncyCastle.NET-ed25519-ad0b6c99d34da50d5473a9c14837a9ce199d0200.tar.xz
ASN.1 updates from bc-java
-rw-r--r--crypto/src/asn1/ASN1StreamParser.cs11
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs110
-rw-r--r--crypto/src/asn1/Asn1OctetString.cs15
-rw-r--r--crypto/src/asn1/BerOctetString.cs103
-rw-r--r--crypto/src/asn1/ConstructedOctetStream.cs45
-rw-r--r--crypto/src/asn1/DefiniteLengthInputStream.cs23
-rw-r--r--crypto/src/asn1/DerBMPString.cs28
-rw-r--r--crypto/src/asn1/DerInteger.cs25
-rw-r--r--crypto/src/asn1/DerOctetString.cs12
-rw-r--r--crypto/src/asn1/LimitedInputStream.cs19
-rw-r--r--crypto/src/asn1/cms/SignedData.cs13
-rw-r--r--crypto/test/src/asn1/test/InputStreamTest.cs63
-rw-r--r--crypto/test/src/openssl/test/ReaderTest.cs6
13 files changed, 320 insertions, 153 deletions
diff --git a/crypto/src/asn1/ASN1StreamParser.cs b/crypto/src/asn1/ASN1StreamParser.cs
index 3eaaadaee..860dc99b1 100644
--- a/crypto/src/asn1/ASN1StreamParser.cs
+++ b/crypto/src/asn1/ASN1StreamParser.cs
@@ -59,7 +59,7 @@ namespace Org.BouncyCastle.Asn1
 			if (_in is IndefiniteLengthInputStream)
 			{
 				if (!constructed)
-					throw new IOException("indefinite length primitive encoding encountered");
+					throw new IOException("indefinite-length primitive encoding encountered");
 
 				return ReadIndef(tag);
 			}
@@ -134,12 +134,13 @@ namespace Org.BouncyCastle.Asn1
 			//
 			// calculate length
 			//
-			int length = Asn1InputStream.ReadLength(_in, _limit);
+			int length = Asn1InputStream.ReadLength(_in, _limit,
+                tagNo == Asn1Tags.OctetString || tagNo == Asn1Tags.Sequence || tagNo == Asn1Tags.Set || tagNo == Asn1Tags.External);
 
-			if (length < 0) // indefinite length method
+			if (length < 0) // indefinite-length method
 			{
 				if (!isConstructed)
-					throw new IOException("indefinite length primitive encoding encountered");
+					throw new IOException("indefinite-length primitive encoding encountered");
 
 				IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in, _limit);
 				Asn1StreamParser sp = new Asn1StreamParser(indIn, _limit);
@@ -158,7 +159,7 @@ namespace Org.BouncyCastle.Asn1
 			}
 			else
 			{
-				DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length);
+				DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length, _limit);
 
 				if ((tag & Asn1Tags.Application) != 0)
 				{
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index 0c7461c98..4f99301b3 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -2,6 +2,7 @@ using System;
 using System.Diagnostics;
 using System.IO;
 
+using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Asn1
@@ -22,10 +23,12 @@ namespace Org.BouncyCastle.Asn1
         internal static int FindLimit(Stream input)
         {
             if (input is LimitedInputStream)
-            {
-                return ((LimitedInputStream)input).GetRemaining();
-            }
-            else if (input is MemoryStream)
+                return ((LimitedInputStream)input).Limit;
+
+            if (input is Asn1InputStream)
+                return ((Asn1InputStream)input).Limit;
+
+            if (input is MemoryStream)
             {
                 MemoryStream mem = (MemoryStream)input;
                 return (int)(mem.Length - mem.Position);
@@ -77,7 +80,7 @@ namespace Org.BouncyCastle.Asn1
         {
             bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
 
-            DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this.s, length);
+            DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this.s, length, limit);
 
             if ((tag & Asn1Tags.Application) != 0)
             {
@@ -95,10 +98,27 @@ namespace Org.BouncyCastle.Asn1
                 switch (tagNo)
                 {
                     case Asn1Tags.OctetString:
+                    {
                         //
                         // yes, people actually do this...
                         //
-                        return new BerOctetString(ReadVector(defIn));
+                        Asn1EncodableVector v = ReadVector(defIn);
+                        Asn1OctetString[] strings = new Asn1OctetString[v.Count];
+
+                        for (int i = 0; i != strings.Length; i++)
+                        {
+                            Asn1Encodable asn1Obj = v[i];
+                            if (!(asn1Obj is Asn1OctetString))
+                            {
+                                throw new Asn1Exception("unknown object encountered in constructed OCTET STRING: "
+                                    + Platform.GetTypeName(asn1Obj));
+                            }
+
+                            strings[i] = (Asn1OctetString)asn1Obj;
+                        }
+
+                        return new BerOctetString(strings);
+                    }
                     case Asn1Tags.Sequence:
                         return CreateDerSequence(defIn);
                     case Asn1Tags.Set:
@@ -162,12 +182,12 @@ namespace Org.BouncyCastle.Asn1
             //
             // calculate length
             //
-            int length = ReadLength(this.s, limit);
+            int length = ReadLength(this.s, limit, false);
 
-            if (length < 0) // indefinite length method
+            if (length < 0) // indefinite-length method
             {
                 if (!isConstructed)
-                    throw new IOException("indefinite length primitive encoding encountered");
+                    throw new IOException("indefinite-length primitive encoding encountered");
 
                 IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this.s, limit);
                 Asn1StreamParser sp = new Asn1StreamParser(indIn, limit);
@@ -210,6 +230,11 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
+        internal virtual int Limit
+        {
+            get { return limit; }
+        }
+
         internal static int ReadTagNumber(
             Stream	s,
             int		tag)
@@ -228,9 +253,7 @@ namespace Org.BouncyCastle.Asn1
                 // 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");
-                }
+                    throw new IOException("corrupted stream - invalid high tag number found");
 
                 while ((b >= 0) && ((b & 0x80) != 0))
                 {
@@ -248,9 +271,7 @@ namespace Org.BouncyCastle.Asn1
             return tagNo;
         }
 
-        internal static int ReadLength(
-            Stream	s,
-            int		limit)
+        internal static int ReadLength(Stream s, int limit, bool isParsing)
         {
             int length = s.ReadByte();
             if (length < 0)
@@ -279,18 +300,18 @@ namespace Org.BouncyCastle.Asn1
                 }
 
                 if (length < 0)
-                    throw new IOException("Corrupted stream - negative length found");
+                    throw new IOException("corrupted stream - negative length found");
 
-                if (length >= limit)   // after all we must have read at least 1 byte
-                    throw new IOException("Corrupted stream - out of bounds length found");
+                if (length >= limit && !isParsing)   // after all we must have read at least 1 byte
+                    throw new IOException("corrupted stream - out of bounds length found: " + length + " >= " + limit);
             }
 
             return length;
         }
 
-        internal static byte[] GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
+        private static byte[] GetBuffer(DefiniteLengthInputStream defIn, byte[][] tmpBuffers)
         {
-            int len = defIn.GetRemaining();
+            int len = defIn.Remaining;
             if (len >= tmpBuffers.Length)
             {
                 return defIn.ToArray();
@@ -307,6 +328,49 @@ namespace Org.BouncyCastle.Asn1
             return buf;
         }
 
+        private static char[] GetBmpCharBuffer(DefiniteLengthInputStream defIn)
+        {
+            int remainingBytes = defIn.Remaining;
+            if (0 != (remainingBytes & 1))
+                throw new IOException("malformed BMPString encoding encountered");
+
+            char[] str = new char[remainingBytes / 2];
+            int stringPos = 0;
+
+            byte[] buf = new byte[8];
+            while (remainingBytes >= 8)
+            {
+                if (Streams.ReadFully(defIn, buf, 0, 8) != 8)
+                    throw new EndOfStreamException("EOF encountered in middle of BMPString");
+
+                str[stringPos    ] = (char)((buf[0] << 8) | (buf[1] & 0xFF));
+                str[stringPos + 1] = (char)((buf[2] << 8) | (buf[3] & 0xFF));
+                str[stringPos + 2] = (char)((buf[4] << 8) | (buf[5] & 0xFF));
+                str[stringPos + 3] = (char)((buf[6] << 8) | (buf[7] & 0xFF));
+                stringPos += 4;
+                remainingBytes -= 8;
+            }
+            if (remainingBytes > 0)
+            {
+                if (Streams.ReadFully(defIn, buf, 0, remainingBytes) != remainingBytes)
+                    throw new EndOfStreamException("EOF encountered in middle of BMPString");
+
+                int bufPos = 0;
+                do
+                {
+                    int b1 = buf[bufPos++] << 8;
+                    int b2 = buf[bufPos++] & 0xFF;
+                    str[stringPos++] = (char)(b1 | b2);
+                }
+                while (bufPos < remainingBytes);
+            }
+
+            if (0 != defIn.Remaining || str.Length != stringPos)
+                throw new InvalidOperationException();
+
+            return str;
+        }
+
         internal static Asn1Object CreatePrimitiveDerObject(
             int                         tagNo,
             DefiniteLengthInputStream   defIn,
@@ -314,6 +378,8 @@ namespace Org.BouncyCastle.Asn1
         {
             switch (tagNo)
             {
+                case Asn1Tags.BmpString:
+                    return new DerBmpString(GetBmpCharBuffer(defIn));
                 case Asn1Tags.Boolean:
                     return DerBoolean.FromOctetString(GetBuffer(defIn, tmpBuffers));
                 case Asn1Tags.Enumerated:
@@ -328,8 +394,6 @@ namespace Org.BouncyCastle.Asn1
             {
                 case Asn1Tags.BitString:
                     return DerBitString.FromAsn1Octets(bytes);
-                case Asn1Tags.BmpString:
-                    return new DerBmpString(bytes);
                 case Asn1Tags.GeneralizedTime:
                     return new DerGeneralizedTime(bytes);
                 case Asn1Tags.GeneralString:
@@ -339,7 +403,7 @@ namespace Org.BouncyCastle.Asn1
                 case Asn1Tags.IA5String:
                     return new DerIA5String(bytes);
                 case Asn1Tags.Integer:
-                    return new DerInteger(bytes);
+                    return new DerInteger(bytes, false);
                 case Asn1Tags.Null:
                     return DerNull.Instance;   // actual content is ignored (enforce 0 length?)
                 case Asn1Tags.NumericString:
diff --git a/crypto/src/asn1/Asn1OctetString.cs b/crypto/src/asn1/Asn1OctetString.cs
index 73b6e51bf..da7dfae46 100644
--- a/crypto/src/asn1/Asn1OctetString.cs
+++ b/crypto/src/asn1/Asn1OctetString.cs
@@ -67,20 +67,7 @@ namespace Org.BouncyCastle.Asn1
 			this.str = str;
         }
 
-        internal Asn1OctetString(
-            Asn1Encodable obj)
-        {
-            try
-            {
-				this.str = obj.GetEncoded(Asn1Encodable.Der);
-            }
-            catch (IOException e)
-            {
-                throw new ArgumentException("Error processing object : " + e.ToString());
-            }
-        }
-
-		public Stream GetOctetStream()
+        public Stream GetOctetStream()
 		{
 			return new MemoryStream(str, false);
 		}
diff --git a/crypto/src/asn1/BerOctetString.cs b/crypto/src/asn1/BerOctetString.cs
index a7c8ad33e..fb4291612 100644
--- a/crypto/src/asn1/BerOctetString.cs
+++ b/crypto/src/asn1/BerOctetString.cs
@@ -9,66 +9,77 @@ namespace Org.BouncyCastle.Asn1
     public class BerOctetString
         : DerOctetString, IEnumerable
     {
-		public static BerOctetString FromSequence(Asn1Sequence seq)
-		{
-			IList v = Platform.CreateArrayList();
-
-			foreach (Asn1Encodable obj in seq)
-			{
-				v.Add(obj);
-			}
-
-			return new BerOctetString(v);
-		}
+        private static readonly int DefaultChunkSize = 1000;
 
-		private const int MaxLength = 1000;
+        public static BerOctetString FromSequence(Asn1Sequence seq)
+        {
+            int count = seq.Count;
+            Asn1OctetString[] v = new Asn1OctetString[count];
+            for (int i = 0; i < count; ++i)
+            {
+                v[i] = Asn1OctetString.GetInstance(seq[i]);
+            }
+            return new BerOctetString(v);
+        }
 
-		/**
-         * convert a vector of octet strings into a single byte string
-         */
-        private static byte[] ToBytes(
-            IEnumerable octs)
+        private static byte[] ToBytes(Asn1OctetString[] octs)
         {
             MemoryStream bOut = new MemoryStream();
-			foreach (DerOctetString o in octs)
-			{
+            foreach (Asn1OctetString o in octs)
+            {
                 byte[] octets = o.GetOctets();
                 bOut.Write(octets, 0, octets.Length);
             }
-			return bOut.ToArray();
+            return bOut.ToArray();
+        }
+
+        private static Asn1OctetString[] ToOctetStringArray(IEnumerable e)
+        {
+            IList list = Platform.CreateArrayList(e);
+
+            int count = list.Count;
+            Asn1OctetString[] v = new Asn1OctetString[count];
+            for (int i = 0; i < count; ++i)
+            {
+                v[i] = Asn1OctetString.GetInstance(list[i]);
+            }
+            return v;
         }
 
-		private readonly IEnumerable octs;
+        private readonly int chunkSize;
+        private readonly Asn1OctetString[] octs;
+
+        [Obsolete("Will be removed")]
+        public BerOctetString(IEnumerable e)
+            : this(ToOctetStringArray(e))
+        {
+        }
 
-		/// <param name="str">The octets making up the octet string.</param>
-		public BerOctetString(
-			byte[] str)
-			: base(str)
+        public BerOctetString(byte[] str)
+			: this(str, DefaultChunkSize)
 		{
 		}
 
-		public BerOctetString(
-			IEnumerable octets)
-			: base(ToBytes(octets))
+        public BerOctetString(Asn1OctetString[] octs)
+            : this(octs, DefaultChunkSize)
         {
-            this.octs = octets;
         }
 
-        public BerOctetString(
-			Asn1Object obj)
-			: base(obj)
+        public BerOctetString(byte[] str, int chunkSize)
+            : this(str, null, chunkSize)
         {
         }
 
-        public BerOctetString(
-			Asn1Encodable obj)
-			: base(obj.ToAsn1Object())
+        public BerOctetString(Asn1OctetString[] octs, int chunkSize)
+            : this(ToBytes(octs), octs, chunkSize)
         {
         }
 
-        public override byte[] GetOctets()
+        private BerOctetString(byte[] str, Asn1OctetString[] octs, int chunkSize)
+            : base(str)
         {
-            return str;
+            this.octs = octs;
+            this.chunkSize = chunkSize;
         }
 
         /**
@@ -93,17 +104,17 @@ namespace Org.BouncyCastle.Asn1
 		private IList GenerateOcts()
         {
             IList vec = Platform.CreateArrayList();
-			for (int i = 0; i < str.Length; i += MaxLength)
-			{
-				int end = System.Math.Min(str.Length, i + MaxLength);
+            for (int i = 0; i < str.Length; i += chunkSize)
+            { 
+				int end = System.Math.Min(str.Length, i + chunkSize);
 
-				byte[] nStr = new byte[end - i];
+                byte[] nStr = new byte[end - i]; 
 
-				Array.Copy(str, i, nStr, 0, nStr.Length);
+                Array.Copy(str, i, nStr, 0, nStr.Length);
 
-				vec.Add(new DerOctetString(nStr));
-			}
-			return vec;
+                vec.Add(new DerOctetString(nStr));
+             } 
+             return vec; 
         }
 
         internal override void Encode(
@@ -118,7 +129,7 @@ namespace Org.BouncyCastle.Asn1
                 //
                 // write out the octet array
                 //
-                foreach (DerOctetString oct in this)
+                foreach (Asn1OctetString oct in this)
                 {
                     derOut.WriteObject(oct);
                 }
diff --git a/crypto/src/asn1/ConstructedOctetStream.cs b/crypto/src/asn1/ConstructedOctetStream.cs
index 1773b22cc..829a9a427 100644
--- a/crypto/src/asn1/ConstructedOctetStream.cs
+++ b/crypto/src/asn1/ConstructedOctetStream.cs
@@ -1,5 +1,6 @@
 using System.IO;
 
+using Org.BouncyCastle.Utilities;
 using Org.BouncyCastle.Utilities.IO;
 
 namespace Org.BouncyCastle.Asn1
@@ -25,13 +26,12 @@ namespace Org.BouncyCastle.Asn1
 				if (!_first)
 					return 0;
 
-				Asn1OctetStringParser s = (Asn1OctetStringParser)_parser.ReadObject();
-
-				if (s == null)
-					return 0;
+                Asn1OctetStringParser next = GetNextParser();
+                if (next == null)
+                    return 0;
 
 				_first = false;
-				_currentStream = s.GetOctetStream();
+				_currentStream = next.GetOctetStream();
 			}
 
 			int totalRead = 0;
@@ -49,15 +49,14 @@ namespace Org.BouncyCastle.Asn1
 				}
 				else
 				{
-					Asn1OctetStringParser aos = (Asn1OctetStringParser)_parser.ReadObject();
-
-					if (aos == null)
+                    Asn1OctetStringParser next = GetNextParser();
+                    if (next == null)
 					{
 						_currentStream = null;
 						return totalRead;
 					}
 
-					_currentStream = aos.GetOctetStream();
+					_currentStream = next.GetOctetStream();
 				}
 			}
 		}
@@ -69,13 +68,12 @@ namespace Org.BouncyCastle.Asn1
 				if (!_first)
 					return 0;
 
-				Asn1OctetStringParser s = (Asn1OctetStringParser)_parser.ReadObject();
-
-				if (s == null)
+                Asn1OctetStringParser next = GetNextParser();
+                if (next == null)
 					return 0;
 
 				_first = false;
-				_currentStream = s.GetOctetStream();
+				_currentStream = next.GetOctetStream();
 			}
 
 			for (;;)
@@ -83,20 +81,29 @@ namespace Org.BouncyCastle.Asn1
 				int b = _currentStream.ReadByte();
 
 				if (b >= 0)
-				{
 					return b;
-				}
-
-				Asn1OctetStringParser aos = (Asn1OctetStringParser)_parser.ReadObject();
 
-				if (aos == null)
+                Asn1OctetStringParser next = GetNextParser();
+                if (next == null)
 				{
 					_currentStream = null;
 					return -1;
 				}
 
-				_currentStream = aos.GetOctetStream();
+				_currentStream = next.GetOctetStream();
 			}
 		}
+
+        private Asn1OctetStringParser GetNextParser()
+        {
+            IAsn1Convertible asn1Obj = _parser.ReadObject();
+            if (asn1Obj == null)
+                return null;
+
+            if (asn1Obj is Asn1OctetStringParser)
+                return (Asn1OctetStringParser)asn1Obj;
+
+            throw new IOException("unknown object encountered: " + Platform.GetTypeName(asn1Obj));
+        }
 	}
 }
diff --git a/crypto/src/asn1/DefiniteLengthInputStream.cs b/crypto/src/asn1/DefiniteLengthInputStream.cs
index 4ae803c0e..d10ea4d12 100644
--- a/crypto/src/asn1/DefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/DefiniteLengthInputStream.cs
@@ -13,10 +13,8 @@ namespace Org.BouncyCastle.Asn1
 		private readonly int _originalLength;
 		private int _remaining;
 
-        internal DefiniteLengthInputStream(
-            Stream	inStream,
-            int		length)
-            : base(inStream, length)
+        internal DefiniteLengthInputStream(Stream inStream, int length, int limit)
+            : base(inStream, limit)
         {
 			if (length < 0)
 				throw new ArgumentException("negative lengths not allowed", "length");
@@ -30,7 +28,7 @@ namespace Org.BouncyCastle.Asn1
 			}
         }
 
-		internal int Remaining
+        internal int Remaining
 		{
 			get { return _remaining; }
 		}
@@ -80,6 +78,14 @@ namespace Org.BouncyCastle.Asn1
             if (_remaining != buf.Length)
                 throw new ArgumentException("buffer length not right for data");
 
+            if (_remaining == 0)
+                return;
+
+            // make sure it's safe to do this!
+            int limit = Limit;
+            if (_remaining >= limit)
+                throw new IOException("corrupted stream - out of bounds length found: " + _remaining + " >= " + limit);
+
             if ((_remaining -= Streams.ReadFully(_in, buf)) != 0)
                 throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
             SetParentEofDetect(true);
@@ -90,7 +96,12 @@ namespace Org.BouncyCastle.Asn1
 			if (_remaining == 0)
 				return EmptyBytes;
 
-			byte[] bytes = new byte[_remaining];
+            // make sure it's safe to do this!
+            int limit = Limit;
+            if (_remaining >= limit)
+                throw new IOException("corrupted stream - out of bounds length found: " + _remaining + " >= " + limit);
+
+            byte[] bytes = new byte[_remaining];
 			if ((_remaining -= Streams.ReadFully(_in, bytes)) != 0)
 				throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
 			SetParentEofDetect(true);
diff --git a/crypto/src/asn1/DerBMPString.cs b/crypto/src/asn1/DerBMPString.cs
index 33d950ff8..061900b9c 100644
--- a/crypto/src/asn1/DerBMPString.cs
+++ b/crypto/src/asn1/DerBMPString.cs
@@ -55,32 +55,44 @@ namespace Org.BouncyCastle.Asn1
 		/**
          * basic constructor - byte encoded string.
          */
-        public DerBmpString(
-            byte[] str)
+        [Obsolete("Will become internal")]
+        public DerBmpString(byte[] str)
         {
 			if (str == null)
 				throw new ArgumentNullException("str");
 
-            char[] cs = new char[str.Length / 2];
+            int byteLen = str.Length;
+            if (0 != (byteLen & 1))
+                throw new ArgumentException("malformed BMPString encoding encountered", "str");
 
-			for (int i = 0; i != cs.Length; i++)
+            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));
             }
 
-			this.str = new string(cs);
+            this.str = new string(cs);
+        }
+
+        internal DerBmpString(char[] str)
+        {
+            if (str == null)
+                throw new ArgumentNullException("str");
+
+            this.str = new string(str);
         }
 
         /**
          * basic constructor
          */
-        public DerBmpString(
-            string str)
+        public DerBmpString(string str)
         {
 			if (str == null)
 				throw new ArgumentNullException("str");
 
-			this.str = str;
+            this.str = str;
         }
 
         public override string GetString()
diff --git a/crypto/src/asn1/DerInteger.cs b/crypto/src/asn1/DerInteger.cs
index 3e19a07b6..4f7b68d35 100644
--- a/crypto/src/asn1/DerInteger.cs
+++ b/crypto/src/asn1/DerInteger.cs
@@ -145,6 +145,18 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
+        public long LongValueExact
+        {
+            get
+            {
+                int count = bytes.Length - start;
+                if (count > 8)
+                    throw new ArithmeticException("ASN.1 Integer out of long range");
+
+                return LongValue(bytes, start, SignExtSigned);
+            }
+        }
+
         internal override void Encode(DerOutputStream derOut)
         {
             derOut.WriteEncoded(Asn1Tags.Integer, bytes);
@@ -182,6 +194,19 @@ namespace Org.BouncyCastle.Asn1
             return val;
         }
 
+        internal static long LongValue(byte[] bytes, int start, int signExt)
+        {
+            int length = bytes.Length;
+            int pos = System.Math.Max(start, length - 8);
+
+            long val = (sbyte)bytes[pos] & signExt;
+            while (++pos < length)
+            {
+                val = (val << 8) | bytes[pos];
+            }
+            return val;
+        }
+
         /**
          * Apply the correct validation for an INTEGER primitive following the BER rules.
          *
diff --git a/crypto/src/asn1/DerOctetString.cs b/crypto/src/asn1/DerOctetString.cs
index c046c9402..d79607950 100644
--- a/crypto/src/asn1/DerOctetString.cs
+++ b/crypto/src/asn1/DerOctetString.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1
 {
     public class DerOctetString
@@ -10,9 +12,13 @@ namespace Org.BouncyCastle.Asn1
         {
         }
 
-		public DerOctetString(
-			Asn1Encodable obj)
-			: base(obj)
+        public DerOctetString(IAsn1Convertible obj)
+            : this(obj.ToAsn1Object())
+        {
+        }
+
+        public DerOctetString(Asn1Encodable obj)
+            : base(obj.GetEncoded(Asn1Encodable.Der))
         {
         }
 
diff --git a/crypto/src/asn1/LimitedInputStream.cs b/crypto/src/asn1/LimitedInputStream.cs
index 62486aa77..98a45876d 100644
--- a/crypto/src/asn1/LimitedInputStream.cs
+++ b/crypto/src/asn1/LimitedInputStream.cs
@@ -8,23 +8,20 @@ namespace Org.BouncyCastle.Asn1
         : BaseInputStream
     {
         protected readonly Stream _in;
-		private int _limit;
+        private int _limit;
 
-        internal LimitedInputStream(
-            Stream	inStream,
-			int		limit)
+        internal LimitedInputStream(Stream inStream, int limit)
         {
             this._in = inStream;
-			this._limit = limit;
+            this._limit = limit;
         }
 
-	    internal virtual int GetRemaining()
-	    {
-	        // TODO: maybe one day this can become more accurate
-	        return _limit;
-	    }
+        internal virtual int Limit
+        {
+            get { return _limit; }
+        }
 
-		protected virtual void SetParentEofDetect(bool on)
+        protected virtual void SetParentEofDetect(bool on)
         {
             if (_in is IndefiniteLengthInputStream)
             {
diff --git a/crypto/src/asn1/cms/SignedData.cs b/crypto/src/asn1/cms/SignedData.cs
index dfc1e2829..5eba8390d 100644
--- a/crypto/src/asn1/cms/SignedData.cs
+++ b/crypto/src/asn1/cms/SignedData.cs
@@ -25,16 +25,13 @@ namespace Org.BouncyCastle.Asn1.Cms
         private readonly bool			certsBer;
         private readonly bool		    crlsBer;
 
-        public static SignedData GetInstance(
-            object obj)
+        public static SignedData GetInstance(object obj)
         {
             if (obj is SignedData)
-                return (SignedData) obj;
-
-            if (obj is Asn1Sequence)
-                return new SignedData((Asn1Sequence) obj);
-
-            throw new ArgumentException("Unknown object in factory: " + Platform.GetTypeName(obj), "obj");
+                return (SignedData)obj;
+            if (obj == null)
+                return null;
+            return new SignedData(Asn1Sequence.GetInstance(obj));
         }
 
         public SignedData(
diff --git a/crypto/test/src/asn1/test/InputStreamTest.cs b/crypto/test/src/asn1/test/InputStreamTest.cs
index 1d92759e0..4cfb304d1 100644
--- a/crypto/test/src/asn1/test/InputStreamTest.cs
+++ b/crypto/test/src/asn1/test/InputStreamTest.cs
@@ -3,6 +3,7 @@ using System.IO;
 
 using NUnit.Framework;
 
+using Org.BouncyCastle.Utilities.Encoders;
 using Org.BouncyCastle.Utilities.Test;
 
 namespace Org.BouncyCastle.Asn1.Tests
@@ -11,9 +12,15 @@ namespace Org.BouncyCastle.Asn1.Tests
 	public class InputStreamTest
 		: SimpleTest
 	{
-		private static readonly byte[] outOfBoundsLength = new byte[] { (byte)0x30, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
-		private static readonly byte[] negativeLength = new byte[] { (byte)0x30, (byte)0x84, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
-		private static readonly byte[] outsideLimitLength = new byte[] { (byte)0x30, (byte)0x83, (byte)0x0f, (byte)0xff, (byte)0xff };
+        private static readonly byte[] outOfBoundsLength = new byte[] { (byte)0x30, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
+        private static readonly byte[] negativeLength = new byte[] { (byte)0x30, (byte)0x84, (byte)0xff, (byte)0xff, (byte)0xff, (byte)0xff };
+        private static readonly byte[] outsideLimitLength = new byte[] { (byte)0x30, (byte)0x83, (byte)0x0f, (byte)0xff, (byte)0xff };
+
+        private static readonly byte[] classCast1 = Base64.Decode("p1AkHmYAvfOEIrL4ESfrNg==");
+        private static readonly byte[] classCast2 = Base64.Decode("JICNbaBUTTq7uxj5mg==");
+        private static readonly byte[] classCast3 = Base64.Decode("JAKzADNCxhrrBSVS");
+        private static readonly byte[] memoryError1 = Base64.Decode("vm66gOiEe+FV/NvujMwSkUp5Lffw5caQlaRU5sdMPC70IGWmyK2/");
+        private static readonly byte[] memoryError2 = Base64.Decode("vm4ogOSEfVGsS3w+KTzb2A0ALYR8VBOQqQeuRwnsPC4AAGWEDLjd");
 
 		public override string Name
 		{
@@ -46,7 +53,7 @@ namespace Org.BouncyCastle.Asn1.Tests
 			}
 			catch (IOException e)
 			{
-				if (!e.Message.Equals("Corrupted stream - negative length found"))
+				if (!e.Message.Equals("corrupted stream - negative length found"))
 				{
 					Fail("wrong exception: " + e.Message);
 				}
@@ -61,12 +68,54 @@ namespace Org.BouncyCastle.Asn1.Tests
 			}
 			catch (IOException e)
 			{
-				if (!e.Message.Equals("Corrupted stream - out of bounds length found"))
-				{
+                if (!e.Message.Equals("corrupted stream - out of bounds length found: 1048575 >= 5"))
+                {
 					Fail("wrong exception: " + e.Message);
 				}
 			}
-		}
+
+            DoTestWithByteArray(classCast1, "unknown object encountered: Org.BouncyCastle.Asn1.DerApplicationSpecific");
+            DoTestWithByteArray(classCast2, "unknown object encountered: Org.BouncyCastle.Asn1.BerTaggedObjectParser");
+            DoTestWithByteArray(classCast3, "unknown object encountered in constructed OCTET STRING: Org.BouncyCastle.Asn1.DerTaggedObject");
+
+            DoTestWithByteArray(memoryError1, "corrupted stream - out of bounds length found: 2078365180 >= 39");
+            DoTestWithByteArray(memoryError2, "corrupted stream - out of bounds length found: 2102504523 >= 39");
+        }
+
+        private void DoTestWithByteArray(byte[] data, string message)
+        {
+            try
+            {
+                Asn1InputStream input = new Asn1InputStream(data);
+
+                IAsn1Convertible p;
+                while ((p = input.ReadObject()) != null)
+                {
+                    Asn1Sequence asn1 = Asn1Sequence.GetInstance(p);
+                    for (int i = 0; i < asn1.Count; i++)
+                    {
+                        IAsn1Convertible c = asn1[i];
+                    }
+                }
+            }
+            catch (IOException e)
+            {
+                IsEquals(e.Message, message, e.Message);
+            }
+            // TODO Without InMemoryRepresentable, the IOException may be swapped/wrapped with an Asn1ParsingException
+            catch (Asn1ParsingException e)
+            {
+                Exception messageException = e;
+
+                IOException ioe = e.InnerException as IOException;
+                if (ioe != null)
+                {
+                    messageException = ioe;
+                }
+
+                IsEquals(messageException.Message, message, messageException.Message);
+            }
+        }
 
 		public static void Main(
 			string[] args)
diff --git a/crypto/test/src/openssl/test/ReaderTest.cs b/crypto/test/src/openssl/test/ReaderTest.cs
index c0be5c848..d0bb3661b 100644
--- a/crypto/test/src/openssl/test/ReaderTest.cs
+++ b/crypto/test/src/openssl/test/ReaderTest.cs
@@ -185,8 +185,8 @@ namespace Org.BouncyCastle.OpenSsl.Tests
             doOpenSslDsaTest("rc2_64_cbc");
             doOpenSslRsaTest("rc2_64_cbc");
 
-            doDudPasswordTest("7fd98", 0, "Corrupted stream - out of bounds length found");
-            doDudPasswordTest("ef677", 1, "Corrupted stream - out of bounds length found");
+            doDudPasswordTest("7fd98", 0, "corrupted stream - out of bounds length found: 599005160 >= 447");
+            doDudPasswordTest("ef677", 1, "corrupted stream - out of bounds length found: 2087569732 >= 447");
             doDudPasswordTest("800ce", 2, "unknown tag 26 encountered");
             doDudPasswordTest("b6cd8", 3, "DEF length 81 object truncated by 56");
             doDudPasswordTest("28ce09", 4, "DEF length 110 object truncated by 28");
@@ -202,7 +202,7 @@ namespace Org.BouncyCastle.OpenSsl.Tests
             doDudPasswordTest("5a3d16", 14, "corrupted stream detected");
             doDudPasswordTest("8d0c97", 15, "corrupted stream detected");
             doDudPasswordTest("bc0daf", 16, "corrupted stream detected");
-            doDudPasswordTest("aaf9c4d",17, "Corrupted stream - out of bounds length found");
+            doDudPasswordTest("aaf9c4d", 17, "corrupted stream - out of bounds length found: 1580418590 >= 447");
 
             // encrypted private key test
             pGet = new Password("password".ToCharArray());