308 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			308 lines
		
	
	
		
			7.5 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| using System.Diagnostics;
 | |
| using System.IO;
 | |
| 
 | |
| using Org.BouncyCastle.Asn1.Utilities;
 | |
| using Org.BouncyCastle.Utilities.IO;
 | |
| 
 | |
| namespace Org.BouncyCastle.Asn1
 | |
| {
 | |
| 	/**
 | |
| 	 * a general purpose ASN.1 decoder - note: this class differs from the
 | |
| 	 * others in that it returns null after it has read the last object in
 | |
| 	 * the stream. If an ASN.1 Null is encountered a Der/BER Null object is
 | |
| 	 * returned.
 | |
| 	 */
 | |
| 	public class Asn1InputStream
 | |
| 		: FilterStream
 | |
| 	{
 | |
| 		private readonly int limit;
 | |
| 
 | |
| 		public Asn1InputStream(
 | |
| 			Stream inputStream)
 | |
| 			: this(inputStream, int.MaxValue)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Create an ASN1InputStream where no DER object will be longer than limit.
 | |
| 		 *
 | |
| 		 * @param input stream containing ASN.1 encoded data.
 | |
| 		 * @param limit maximum size of a DER encoded object.
 | |
| 		 */
 | |
| 		public Asn1InputStream(
 | |
| 			Stream	inputStream,
 | |
| 			int		limit)
 | |
| 			: base(inputStream)
 | |
| 		{
 | |
| 			this.limit = limit;
 | |
| 		}
 | |
| 
 | |
| 		/**
 | |
| 		 * Create an ASN1InputStream based on the input byte array. The length of DER objects in
 | |
| 		 * the stream is automatically limited to the length of the input array.
 | |
| 		 *
 | |
| 		 * @param input array containing ASN.1 encoded data.
 | |
| 		 */
 | |
| 		public Asn1InputStream(
 | |
| 			byte[] input)
 | |
| 			: this(new MemoryStream(input, false), input.Length)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		internal Asn1EncodableVector BuildEncodableVector()
 | |
| 		{
 | |
| 			Asn1EncodableVector v = new Asn1EncodableVector();
 | |
| 
 | |
| 			Asn1Object o;
 | |
| 			while ((o = ReadObject()) != null)
 | |
| 			{
 | |
| 				v.Add(o);
 | |
| 			}
 | |
| 
 | |
| 			return v;
 | |
| 		}
 | |
| 
 | |
| 		internal virtual Asn1EncodableVector BuildDerEncodableVector(
 | |
| 			DefiniteLengthInputStream dIn)
 | |
| 		{
 | |
| 			return new Asn1InputStream(dIn).BuildEncodableVector();
 | |
| 		}
 | |
| 
 | |
| 		internal virtual DerSequence CreateDerSequence(
 | |
| 			DefiniteLengthInputStream dIn)
 | |
| 		{
 | |
| 			return DerSequence.FromVector(BuildDerEncodableVector(dIn));
 | |
| 		}
 | |
| 
 | |
| 		internal virtual DerSet CreateDerSet(
 | |
| 			DefiniteLengthInputStream dIn)
 | |
| 		{
 | |
| 			return DerSet.FromVector(BuildDerEncodableVector(dIn), false);
 | |
| 		}
 | |
| 
 | |
| 		public Asn1Object ReadObject()
 | |
| 		{
 | |
| 			int tag = ReadByte();
 | |
| 			if (tag <= 0)
 | |
| 			{
 | |
| 				if (tag == 0)
 | |
| 					throw new IOException("unexpected end-of-contents marker");
 | |
| 
 | |
| 				return null;
 | |
| 			}
 | |
| 
 | |
| 			//
 | |
| 			// calculate tag number
 | |
| 			//
 | |
| 			int tagNo = 0;
 | |
| 			if ((tag & Asn1Tags.Tagged) != 0 || (tag & Asn1Tags.Application) != 0)
 | |
| 			{
 | |
| 				tagNo = ReadTagNumber(this, tag);
 | |
| 			}
 | |
| 
 | |
| 			bool isConstructed = (tag & Asn1Tags.Constructed) != 0;
 | |
| 			int baseTagNo = tag & ~Asn1Tags.Constructed;
 | |
| 
 | |
| 			//
 | |
| 			// calculate length
 | |
| 			//
 | |
| 			int length = ReadLength(this, limit);
 | |
| 
 | |
| 			if (length < 0) // indefinite length method
 | |
| 			{
 | |
| 				if (!isConstructed)
 | |
| 					throw new IOException("indefinite length primitive encoding encountered");
 | |
| 
 | |
| 				IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this);
 | |
| 
 | |
| 				if ((tag & Asn1Tags.Tagged) != 0)
 | |
| 				{
 | |
| 					return new BerTaggedObjectParser(tag, tagNo, indIn).ToAsn1Object();
 | |
| 				}
 | |
| 
 | |
| 				Asn1StreamParser sp = new Asn1StreamParser(indIn);
 | |
| 
 | |
| 				// TODO There are other tags that may be constructed (e.g. BitString)
 | |
| 				switch (baseTagNo)
 | |
| 				{
 | |
| 					case Asn1Tags.OctetString:
 | |
| 						return new BerOctetStringParser(sp).ToAsn1Object();
 | |
| 					case Asn1Tags.Sequence:
 | |
| 						return new BerSequenceParser(sp).ToAsn1Object();
 | |
| 					case Asn1Tags.Set:
 | |
| 						return new BerSetParser(sp).ToAsn1Object();
 | |
| 					default:
 | |
| 						throw new IOException("unknown BER object encountered");
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length);
 | |
| 
 | |
| 				if ((tag & Asn1Tags.Application) != 0)
 | |
| 				{
 | |
| 					return new DerApplicationSpecific(isConstructed, tagNo, defIn.ToArray());
 | |
| 				}
 | |
| 
 | |
| 				if ((tag & Asn1Tags.Tagged) != 0)
 | |
| 				{
 | |
| 					return new BerTaggedObjectParser(tag, tagNo, defIn).ToAsn1Object();
 | |
| 				}
 | |
| 
 | |
| 				if (isConstructed)
 | |
| 				{
 | |
| 					// TODO There are other tags that may be constructed (e.g. BitString)
 | |
| 					switch (baseTagNo)
 | |
| 					{
 | |
| 						case Asn1Tags.OctetString:
 | |
| 							//
 | |
| 							// yes, people actually do this...
 | |
| 							//
 | |
| 							return new BerOctetString(BuildDerEncodableVector(defIn));
 | |
| 						case Asn1Tags.Sequence:
 | |
| 							return CreateDerSequence(defIn);
 | |
| 						case Asn1Tags.Set:
 | |
| 							return CreateDerSet(defIn);
 | |
| 						default:
 | |
| 							return new DerUnknownTag(tag, defIn.ToArray());
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				return CreatePrimitiveDerObject(tag, defIn.ToArray());
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		internal static int ReadTagNumber(
 | |
| 			Stream	s,
 | |
| 			int		tag)
 | |
| 		{
 | |
| 			int tagNo = tag & 0x1f;
 | |
| 
 | |
| 			//
 | |
| 			// with tagged object tag number is bottom 5 bits, or stored at the start of the content
 | |
| 			//
 | |
| 			if (tagNo == 0x1f)
 | |
| 			{
 | |
| 				tagNo = 0;
 | |
| 
 | |
| 				int b = s.ReadByte();
 | |
| 
 | |
| 				// 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 >= 0) && ((b & 0x80) != 0))
 | |
| 				{
 | |
| 					tagNo |= (b & 0x7f);
 | |
| 					tagNo <<= 7;
 | |
| 					b = s.ReadByte();
 | |
| 				}
 | |
| 
 | |
| 				if (b < 0)
 | |
| 					throw new EndOfStreamException("EOF found inside tag value.");
 | |
| 
 | |
| 				tagNo |= (b & 0x7f);
 | |
| 			}
 | |
| 
 | |
| 			return tagNo;
 | |
| 		}
 | |
| 
 | |
| 		internal static int ReadLength(
 | |
| 			Stream	s,
 | |
| 			int		limit)
 | |
| 		{
 | |
| 			int length = s.ReadByte();
 | |
| 			if (length < 0)
 | |
| 				throw new EndOfStreamException("EOF found when length expected");
 | |
| 
 | |
| 			if (length == 0x80)
 | |
| 				return -1;      // indefinite-length encoding
 | |
| 
 | |
| 			if (length > 127)
 | |
| 			{
 | |
| 				int size = length & 0x7f;
 | |
| 
 | |
| 				if (size > 4)
 | |
| 					throw new IOException("DER length more than 4 bytes");
 | |
| 
 | |
| 				length = 0;
 | |
| 				for (int i = 0; i < size; i++)
 | |
| 				{
 | |
| 					int next = s.ReadByte();
 | |
| 
 | |
| 					if (next < 0)
 | |
| 						throw new EndOfStreamException("EOF found reading length");
 | |
| 
 | |
| 					length = (length << 8) + next;
 | |
| 				}
 | |
| 
 | |
| 				if (length < 0)
 | |
| 					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");
 | |
| 			}
 | |
| 
 | |
| 			return length;
 | |
| 		}
 | |
| 
 | |
| 		internal static Asn1Object CreatePrimitiveDerObject(
 | |
| 			int		tag,
 | |
| 			byte[]	bytes)
 | |
| 		{
 | |
| 			Debug.Assert((tag & (Asn1Tags.Application | Asn1Tags.Constructed | Asn1Tags.Tagged)) == 0);
 | |
| 
 | |
| 			switch (tag)
 | |
| 			{
 | |
| 				case Asn1Tags.BitString:
 | |
| 				{
 | |
| 					int padBits = bytes[0];
 | |
| 					byte[] data = new byte[bytes.Length - 1];
 | |
| 					Array.Copy(bytes, 1, data, 0, bytes.Length - 1);
 | |
| 					return new DerBitString(data, padBits);
 | |
| 				}
 | |
| 				case Asn1Tags.BmpString:
 | |
| 					return new DerBmpString(bytes);
 | |
| 				case Asn1Tags.Boolean:
 | |
| 					return new DerBoolean(bytes);
 | |
| 				case Asn1Tags.Enumerated:
 | |
| 					return new DerEnumerated(bytes);
 | |
| 				case Asn1Tags.GeneralizedTime:
 | |
| 					return new DerGeneralizedTime(bytes);
 | |
| 				case Asn1Tags.GeneralString:
 | |
| 					return new DerGeneralString(bytes);
 | |
| 				case Asn1Tags.IA5String:
 | |
| 					return new DerIA5String(bytes);
 | |
| 				case Asn1Tags.Integer:
 | |
| 					return new DerInteger(bytes);
 | |
| 				case Asn1Tags.Null:
 | |
| 					return DerNull.Instance;   // actual content is ignored (enforce 0 length?)
 | |
| 				case Asn1Tags.NumericString:
 | |
| 					return new DerNumericString(bytes);
 | |
| 				case Asn1Tags.ObjectIdentifier:
 | |
| 					return new DerObjectIdentifier(bytes);
 | |
| 				case Asn1Tags.OctetString:
 | |
| 					return new DerOctetString(bytes);
 | |
| 				case Asn1Tags.PrintableString:
 | |
| 					return new DerPrintableString(bytes);
 | |
| 				case Asn1Tags.T61String:
 | |
| 					return new DerT61String(bytes);
 | |
| 				case Asn1Tags.UniversalString:
 | |
| 					return new DerUniversalString(bytes);
 | |
| 				case Asn1Tags.UtcTime:
 | |
| 					return new DerUtcTime(bytes);
 | |
| 				case Asn1Tags.Utf8String:
 | |
| 					return new DerUtf8String(bytes);
 | |
| 				case Asn1Tags.VisibleString:
 | |
| 					return new DerVisibleString(bytes);
 | |
| 				default:
 | |
| 					return new DerUnknownTag(tag, bytes);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| }
 |