summary refs log tree commit diff
path: root/crypto/src/asn1/cms
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/asn1/cms')
-rw-r--r--crypto/src/asn1/cms/Attribute.cs70
-rw-r--r--crypto/src/asn1/cms/AttributeTable.cs231
-rw-r--r--crypto/src/asn1/cms/Attributes.cs55
-rw-r--r--crypto/src/asn1/cms/AuthEnvelopedData.cs203
-rw-r--r--crypto/src/asn1/cms/AuthEnvelopedDataParser.cs145
-rw-r--r--crypto/src/asn1/cms/AuthenticatedData.cs270
-rw-r--r--crypto/src/asn1/cms/AuthenticatedDataParser.cs182
-rw-r--r--crypto/src/asn1/cms/CMSAttributes.cs14
-rw-r--r--crypto/src/asn1/cms/CMSObjectIdentifiers.cs28
-rw-r--r--crypto/src/asn1/cms/CompressedData.cs96
-rw-r--r--crypto/src/asn1/cms/CompressedDataParser.cs47
-rw-r--r--crypto/src/asn1/cms/ContentInfo.cs88
-rw-r--r--crypto/src/asn1/cms/ContentInfoParser.cs40
-rw-r--r--crypto/src/asn1/cms/EncryptedContentInfo.cs94
-rw-r--r--crypto/src/asn1/cms/EncryptedContentInfoParser.cs46
-rw-r--r--crypto/src/asn1/cms/EncryptedData.cs95
-rw-r--r--crypto/src/asn1/cms/EnvelopedData.cs176
-rw-r--r--crypto/src/asn1/cms/EnvelopedDataParser.cs107
-rw-r--r--crypto/src/asn1/cms/Evidence.cs47
-rw-r--r--crypto/src/asn1/cms/IssuerAndSerialNumber.cs64
-rw-r--r--crypto/src/asn1/cms/KEKIdentifier.cs119
-rw-r--r--crypto/src/asn1/cms/KEKRecipientInfo.cs106
-rw-r--r--crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs92
-rw-r--r--crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs141
-rw-r--r--crypto/src/asn1/cms/KeyTransRecipientInfo.cs99
-rw-r--r--crypto/src/asn1/cms/MetaData.cs94
-rw-r--r--crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs168
-rw-r--r--crypto/src/asn1/cms/OriginatorInfo.cs121
-rw-r--r--crypto/src/asn1/cms/OriginatorPublicKey.cs87
-rw-r--r--crypto/src/asn1/cms/OtherKeyAttribute.cs70
-rw-r--r--crypto/src/asn1/cms/OtherRecipientInfo.cs83
-rw-r--r--crypto/src/asn1/cms/OtherRevocationInfoFormat.cs77
-rw-r--r--crypto/src/asn1/cms/PasswordRecipientInfo.cs133
-rw-r--r--crypto/src/asn1/cms/RecipientEncryptedKey.cs88
-rw-r--r--crypto/src/asn1/cms/RecipientIdentifier.cs89
-rw-r--r--crypto/src/asn1/cms/RecipientInfo.cs145
-rw-r--r--crypto/src/asn1/cms/RecipientKeyIdentifier.cs137
-rw-r--r--crypto/src/asn1/cms/SCVPReqRes.cs77
-rw-r--r--crypto/src/asn1/cms/SignedData.cs287
-rw-r--r--crypto/src/asn1/cms/SignedDataParser.cs112
-rw-r--r--crypto/src/asn1/cms/SignerIdentifier.cs89
-rw-r--r--crypto/src/asn1/cms/SignerInfo.cs185
-rw-r--r--crypto/src/asn1/cms/Time.cs118
-rw-r--r--crypto/src/asn1/cms/TimeStampAndCRL.cs62
-rw-r--r--crypto/src/asn1/cms/TimeStampTokenEvidence.cs65
-rw-r--r--crypto/src/asn1/cms/TimeStampedData.cs95
-rw-r--r--crypto/src/asn1/cms/TimeStampedDataParser.cs76
-rw-r--r--crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs103
48 files changed, 5216 insertions, 0 deletions
diff --git a/crypto/src/asn1/cms/Attribute.cs b/crypto/src/asn1/cms/Attribute.cs
new file mode 100644
index 000000000..c4a104a3f
--- /dev/null
+++ b/crypto/src/asn1/cms/Attribute.cs
@@ -0,0 +1,70 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class Attribute
+        : Asn1Encodable
+    {
+        private DerObjectIdentifier	attrType;
+        private Asn1Set				attrValues;
+
+		/**
+        * return an Attribute object from the given object.
+        *
+        * @param o the object we want converted.
+        * @exception ArgumentException if the object cannot be converted.
+        */
+        public static Attribute GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is Attribute)
+				return (Attribute) obj;
+
+			if (obj is Asn1Sequence)
+                return new Attribute((Asn1Sequence) obj);
+
+			throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj");
+        }
+
+		public Attribute(
+            Asn1Sequence seq)
+        {
+            attrType = (DerObjectIdentifier)seq[0];
+            attrValues = (Asn1Set)seq[1];
+        }
+
+		public Attribute(
+            DerObjectIdentifier attrType,
+            Asn1Set             attrValues)
+        {
+            this.attrType = attrType;
+            this.attrValues = attrValues;
+        }
+
+        public DerObjectIdentifier AttrType
+		{
+			get { return attrType; }
+		}
+
+		public Asn1Set AttrValues
+		{
+			get { return attrValues; }
+		}
+
+		/**
+        * Produce an object suitable for an Asn1OutputStream.
+        * <pre>
+        * Attribute ::= SEQUENCE {
+        *     attrType OBJECT IDENTIFIER,
+        *     attrValues SET OF AttributeValue
+        * }
+        * </pre>
+        */
+        public override Asn1Object ToAsn1Object()
+        {
+			return new DerSequence(attrType, attrValues);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/AttributeTable.cs b/crypto/src/asn1/cms/AttributeTable.cs
new file mode 100644
index 000000000..8a3ee5d0e
--- /dev/null
+++ b/crypto/src/asn1/cms/AttributeTable.cs
@@ -0,0 +1,231 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class AttributeTable
+    {
+        private readonly IDictionary attributes;
+
+#if !SILVERLIGHT
+        [Obsolete]
+        public AttributeTable(
+            Hashtable attrs)
+        {
+            this.attributes = Platform.CreateHashtable(attrs);
+        }
+#endif
+
+        public AttributeTable(
+            IDictionary attrs)
+        {
+            this.attributes = Platform.CreateHashtable(attrs);
+        }
+
+        public AttributeTable(
+            Asn1EncodableVector v)
+        {
+            this.attributes = Platform.CreateHashtable(v.Count);
+
+			foreach (Asn1Encodable o in v)
+            {
+                Attribute a = Attribute.GetInstance(o);
+
+				AddAttribute(a);
+            }
+        }
+
+        public AttributeTable(
+            Asn1Set s)
+        {
+            this.attributes = Platform.CreateHashtable(s.Count);
+
+			for (int i = 0; i != s.Count; i++)
+            {
+                Attribute a = Attribute.GetInstance(s[i]);
+
+                AddAttribute(a);
+            }
+        }
+
+		public AttributeTable(
+			Attributes attrs)
+			: this(Asn1Set.GetInstance(attrs.ToAsn1Object()))
+		{
+		}
+
+		private void AddAttribute(
+            Attribute a)
+        {
+			DerObjectIdentifier oid = a.AttrType;
+            object obj = attributes[oid];
+
+            if (obj == null)
+            {
+                attributes[oid] = a;
+            }
+            else
+            {
+                IList v;
+
+                if (obj is Attribute)
+                {
+                    v = Platform.CreateArrayList();
+
+                    v.Add(obj);
+                    v.Add(a);
+                }
+                else
+                {
+                    v = (IList) obj;
+
+                    v.Add(a);
+                }
+
+                attributes[oid] = v;
+            }
+        }
+
+		/// <summary>Return the first attribute matching the given OBJECT IDENTIFIER</summary>
+		public Attribute this[DerObjectIdentifier oid]
+		{
+			get
+			{
+				object obj = attributes[oid];
+
+				if (obj is IList)
+				{
+					return (Attribute)((IList)obj)[0];
+				}
+
+				return (Attribute) obj;
+			}
+		}
+
+		[Obsolete("Use 'object[oid]' syntax instead")]
+        public Attribute Get(
+            DerObjectIdentifier oid)
+        {
+			return this[oid];
+        }
+
+		/**
+        * Return all the attributes matching the OBJECT IDENTIFIER oid. The vector will be
+        * empty if there are no attributes of the required type present.
+        *
+        * @param oid type of attribute required.
+        * @return a vector of all the attributes found of type oid.
+        */
+        public Asn1EncodableVector GetAll(
+            DerObjectIdentifier oid)
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector();
+
+            object obj = attributes[oid];
+
+			if (obj is IList)
+            {
+                foreach (Attribute a in (IList)obj)
+                {
+                    v.Add(a);
+                }
+            }
+            else if (obj != null)
+            {
+                v.Add((Attribute) obj);
+            }
+
+			return v;
+        }
+
+		public int Count
+		{
+			get
+			{
+				int total = 0;
+
+				foreach (object o in attributes.Values)
+				{
+					if (o is IList)
+					{
+						total += ((IList)o).Count;
+					}
+					else
+					{
+						++total;
+					}
+				}
+
+				return total;
+			}
+		}
+
+        public IDictionary ToDictionary()
+        {
+            return Platform.CreateHashtable(attributes);
+        }
+
+#if !SILVERLIGHT
+        [Obsolete("Use 'ToDictionary' instead")]
+		public Hashtable ToHashtable()
+        {
+            return new Hashtable(attributes);
+        }
+#endif
+
+		public Asn1EncodableVector ToAsn1EncodableVector()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector();
+
+			foreach (object obj in attributes.Values)
+            {
+                if (obj is IList)
+                {
+                    foreach (object el in (IList)obj)
+                    {
+                        v.Add(Attribute.GetInstance(el));
+                    }
+                }
+                else
+                {
+                    v.Add(Attribute.GetInstance(obj));
+                }
+            }
+
+			return v;
+        }
+
+		public Attributes ToAttributes()
+		{
+			return new Attributes(this.ToAsn1EncodableVector());
+		}
+
+		/**
+		 * Return a new table with the passed in attribute added.
+		 *
+		 * @param attrType
+		 * @param attrValue
+		 * @return
+		 */
+		public AttributeTable Add(DerObjectIdentifier attrType, Asn1Encodable attrValue)
+		{
+			AttributeTable newTable = new AttributeTable(attributes);
+
+			newTable.AddAttribute(new Attribute(attrType, new DerSet(attrValue)));
+
+			return newTable;
+		}
+
+		public AttributeTable Remove(DerObjectIdentifier attrType)
+		{
+			AttributeTable newTable = new AttributeTable(attributes);
+
+			newTable.attributes.Remove(attrType);
+
+			return newTable;
+		}
+    }
+}
diff --git a/crypto/src/asn1/cms/Attributes.cs b/crypto/src/asn1/cms/Attributes.cs
new file mode 100644
index 000000000..5b6b13034
--- /dev/null
+++ b/crypto/src/asn1/cms/Attributes.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class Attributes
+        : Asn1Encodable
+    {
+        private readonly Asn1Set attributes;
+
+        private Attributes(Asn1Set attributes)
+        {
+            this.attributes = attributes;
+        }
+
+        public Attributes(Asn1EncodableVector v)
+        {
+            attributes = new BerSet(v);
+        }
+
+        public static Attributes GetInstance(object obj)
+        {
+            if (obj is Attributes)
+                return (Attributes)obj;
+
+            if (obj != null)
+                return new Attributes(Asn1Set.GetInstance(obj));
+
+            return null;
+        }
+
+        public virtual Attribute[] GetAttributes()
+        {
+            Attribute[] rv = new Attribute[attributes.Count];
+
+            for (int i = 0; i != rv.Length; i++)
+            {
+                rv[i] = Attribute.GetInstance(attributes[i]);
+            }
+
+            return rv;
+        }
+
+        /**
+         * <pre>
+         * Attributes ::=
+         *   SET SIZE(1..MAX) OF Attribute -- according to RFC 5652
+         * </pre>
+         * @return
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return attributes;
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/AuthEnvelopedData.cs b/crypto/src/asn1/cms/AuthEnvelopedData.cs
new file mode 100644
index 000000000..4260d80f9
--- /dev/null
+++ b/crypto/src/asn1/cms/AuthEnvelopedData.cs
@@ -0,0 +1,203 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class AuthEnvelopedData
+		: Asn1Encodable
+	{
+		private DerInteger				version;
+		private OriginatorInfo			originatorInfo;
+		private Asn1Set					recipientInfos;
+		private EncryptedContentInfo	authEncryptedContentInfo;
+		private Asn1Set					authAttrs;
+		private Asn1OctetString			mac;
+		private Asn1Set					unauthAttrs;
+
+		public AuthEnvelopedData(
+			OriginatorInfo			originatorInfo,
+			Asn1Set					recipientInfos,
+			EncryptedContentInfo	authEncryptedContentInfo,
+			Asn1Set					authAttrs,
+			Asn1OctetString			mac,
+			Asn1Set					unauthAttrs)
+		{
+			// "It MUST be set to 0."
+			this.version = new DerInteger(0);
+
+			this.originatorInfo = originatorInfo;
+
+			// TODO
+			// "There MUST be at least one element in the collection."
+			this.recipientInfos = recipientInfos;
+
+			this.authEncryptedContentInfo = authEncryptedContentInfo;
+
+			// TODO
+			// "The authAttrs MUST be present if the content type carried in
+			// EncryptedContentInfo is not id-data."
+			this.authAttrs = authAttrs;
+
+			this.mac = mac;
+
+			this.unauthAttrs = unauthAttrs;
+	    }
+
+		private AuthEnvelopedData(
+			Asn1Sequence	seq)
+		{
+			int index = 0;
+
+			// TODO
+			// "It MUST be set to 0."
+			Asn1Object tmp = seq[index++].ToAsn1Object();
+			version = (DerInteger)tmp;
+
+			tmp = seq[index++].ToAsn1Object();
+			if (tmp is Asn1TaggedObject)
+			{
+				originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject)tmp, false);
+				tmp = seq[index++].ToAsn1Object();
+			}
+
+			// TODO
+			// "There MUST be at least one element in the collection."
+			recipientInfos = Asn1Set.GetInstance(tmp);
+
+			tmp = seq[index++].ToAsn1Object();
+			authEncryptedContentInfo = EncryptedContentInfo.GetInstance(tmp);
+
+			tmp = seq[index++].ToAsn1Object();
+			if (tmp is Asn1TaggedObject)
+			{
+				authAttrs = Asn1Set.GetInstance((Asn1TaggedObject)tmp, false);
+				tmp = seq[index++].ToAsn1Object();
+			}
+			else
+			{
+				// TODO
+				// "The authAttrs MUST be present if the content type carried in
+				// EncryptedContentInfo is not id-data."
+			}
+
+			mac = Asn1OctetString.GetInstance(tmp);
+
+			if (seq.Count > index)
+			{
+				tmp = seq[index++].ToAsn1Object();
+				unauthAttrs = Asn1Set.GetInstance((Asn1TaggedObject)tmp, false);
+			}
+		}
+
+		/**
+		 * return an AuthEnvelopedData object 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.
+		 * @throws ArgumentException if the object held by the
+		 *                                  tagged object cannot be converted.
+		 */
+		public static AuthEnvelopedData GetInstance(
+			Asn1TaggedObject	obj,
+			bool				isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+
+		/**
+		 * return an AuthEnvelopedData object from the given object.
+		 *
+		 * @param obj the object we want converted.
+		 * @throws ArgumentException if the object cannot be converted.
+		 */
+		public static AuthEnvelopedData GetInstance(
+			object	obj)
+		{
+			if (obj == null || obj is AuthEnvelopedData)
+				return (AuthEnvelopedData)obj;
+
+			if (obj is Asn1Sequence)
+				return new AuthEnvelopedData((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid AuthEnvelopedData: " + obj.GetType().Name);
+		}
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public OriginatorInfo OriginatorInfo
+		{
+			get { return originatorInfo; }
+		}
+
+		public Asn1Set RecipientInfos
+		{
+			get { return recipientInfos; }
+		}
+
+		public EncryptedContentInfo AuthEncryptedContentInfo
+		{
+			get { return authEncryptedContentInfo; }
+		}
+
+		public Asn1Set AuthAttrs
+		{
+			get { return authAttrs; }
+		}
+
+		public Asn1OctetString Mac
+		{
+			get { return mac; }
+		}
+
+		public Asn1Set UnauthAttrs
+		{
+			get { return unauthAttrs; }
+		}
+
+		/**
+		 * Produce an object suitable for an Asn1OutputStream.
+		 * <pre>
+		 * AuthEnvelopedData ::= SEQUENCE {
+		 *   version CMSVersion,
+		 *   originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+		 *   recipientInfos RecipientInfos,
+		 *   authEncryptedContentInfo EncryptedContentInfo,
+		 *   authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+		 *   mac MessageAuthenticationCode,
+		 *   unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
+		 * </pre>
+		 */
+	    public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(version);
+
+			if (originatorInfo != null)
+			{
+				v.Add(new DerTaggedObject(false, 0, originatorInfo));
+			}
+
+			v.Add(recipientInfos, authEncryptedContentInfo);
+
+			// "authAttrs optionally contains the authenticated attributes."
+			if (authAttrs != null)
+			{
+				// "AuthAttributes MUST be DER encoded, even if the rest of the
+				// AuthEnvelopedData structure is BER encoded."
+				v.Add(new DerTaggedObject(false, 1, authAttrs));
+			}
+
+			v.Add(mac);
+
+			// "unauthAttrs optionally contains the unauthenticated attributes."
+			if (unauthAttrs != null)
+			{
+				v.Add(new DerTaggedObject(false, 2, unauthAttrs));
+			}
+
+			return new BerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/AuthEnvelopedDataParser.cs b/crypto/src/asn1/cms/AuthEnvelopedDataParser.cs
new file mode 100644
index 000000000..35cb3bfcc
--- /dev/null
+++ b/crypto/src/asn1/cms/AuthEnvelopedDataParser.cs
@@ -0,0 +1,145 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	 * Produce an object suitable for an Asn1OutputStream.
+	 * 
+	 * <pre>
+	 * AuthEnvelopedData ::= SEQUENCE {
+	 *   version CMSVersion,
+	 *   originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+	 *   recipientInfos RecipientInfos,
+	 *   authEncryptedContentInfo EncryptedContentInfo,
+	 *   authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+	 *   mac MessageAuthenticationCode,
+	 *   unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
+	 * </pre>
+	*/
+	public class AuthEnvelopedDataParser
+	{
+		private Asn1SequenceParser	seq;
+		private DerInteger			version;
+		private IAsn1Convertible	nextObject;
+		private bool				originatorInfoCalled;
+
+		public AuthEnvelopedDataParser(
+			Asn1SequenceParser	seq)
+		{
+			this.seq = seq;
+
+			// TODO
+			// "It MUST be set to 0."
+			this.version = (DerInteger)seq.ReadObject();
+		}
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public OriginatorInfo GetOriginatorInfo()
+		{
+			originatorInfoCalled = true;
+
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			if (nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)nextObject).TagNo == 0)
+			{
+				Asn1SequenceParser originatorInfo = (Asn1SequenceParser) ((Asn1TaggedObjectParser)nextObject).GetObjectParser(Asn1Tags.Sequence, false);
+				nextObject = null;
+				return OriginatorInfo.GetInstance(originatorInfo.ToAsn1Object());
+			}
+
+			return null;
+		}
+
+		public Asn1SetParser GetRecipientInfos()
+		{
+			if (!originatorInfoCalled)
+			{
+				GetOriginatorInfo();
+			}
+
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			Asn1SetParser recipientInfos = (Asn1SetParser)nextObject;
+			nextObject = null;
+			return recipientInfos;
+		}
+
+		public EncryptedContentInfoParser GetAuthEncryptedContentInfo() 
+		{
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			if (nextObject != null)
+			{
+				Asn1SequenceParser o = (Asn1SequenceParser) nextObject;
+				nextObject = null;
+				return new EncryptedContentInfoParser(o);
+			}
+
+			return null;
+		}
+		
+		public Asn1SetParser GetAuthAttrs()
+		{
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			if (nextObject is Asn1TaggedObjectParser)
+			{
+				IAsn1Convertible o = nextObject;
+				nextObject = null;
+				return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false);
+			}
+
+			// TODO
+			// "The authAttrs MUST be present if the content type carried in
+			// EncryptedContentInfo is not id-data."
+
+			return null;
+		}
+		
+		public Asn1OctetString GetMac()
+		{
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			IAsn1Convertible o = nextObject;
+			nextObject = null;
+
+			return Asn1OctetString.GetInstance(o.ToAsn1Object());
+		}
+		
+		public Asn1SetParser GetUnauthAttrs()
+		{
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			if (nextObject != null)
+			{
+				IAsn1Convertible o = nextObject;
+				nextObject = null;
+				return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false);
+			}
+
+			return null;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/AuthenticatedData.cs b/crypto/src/asn1/cms/AuthenticatedData.cs
new file mode 100644
index 000000000..15286d1aa
--- /dev/null
+++ b/crypto/src/asn1/cms/AuthenticatedData.cs
@@ -0,0 +1,270 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class AuthenticatedData
+		: Asn1Encodable
+	{
+		private DerInteger version;
+		private OriginatorInfo originatorInfo;
+		private Asn1Set recipientInfos;
+		private AlgorithmIdentifier macAlgorithm;
+		private AlgorithmIdentifier digestAlgorithm;
+		private ContentInfo encapsulatedContentInfo;
+		private Asn1Set authAttrs;
+		private Asn1OctetString mac;
+		private Asn1Set unauthAttrs;
+
+		public AuthenticatedData(
+			OriginatorInfo		originatorInfo,
+			Asn1Set				recipientInfos,
+			AlgorithmIdentifier	macAlgorithm,
+			AlgorithmIdentifier	digestAlgorithm,
+			ContentInfo			encapsulatedContent,
+			Asn1Set				authAttrs,
+			Asn1OctetString		mac,
+			Asn1Set				unauthAttrs)
+		{
+			if (digestAlgorithm != null || authAttrs != null)
+			{
+				if (digestAlgorithm == null || authAttrs == null)
+				{
+					throw new ArgumentException("digestAlgorithm and authAttrs must be set together");
+				}
+			}
+
+			version = new DerInteger(CalculateVersion(originatorInfo));
+
+			this.originatorInfo = originatorInfo;
+			this.macAlgorithm = macAlgorithm;
+			this.digestAlgorithm = digestAlgorithm;
+			this.recipientInfos = recipientInfos;
+			this.encapsulatedContentInfo = encapsulatedContent;
+			this.authAttrs = authAttrs;
+			this.mac = mac;
+			this.unauthAttrs = unauthAttrs;
+		}
+
+		private AuthenticatedData(
+			Asn1Sequence	seq)
+		{
+			int index = 0;
+
+			version = (DerInteger)seq[index++];
+
+			Asn1Encodable tmp = seq[index++];
+			if (tmp is Asn1TaggedObject)
+			{
+				originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject)tmp, false);
+				tmp = seq[index++];
+			}
+
+			recipientInfos = Asn1Set.GetInstance(tmp);
+			macAlgorithm = AlgorithmIdentifier.GetInstance(seq[index++]);
+
+			tmp = seq[index++];
+			if (tmp is Asn1TaggedObject)
+			{
+				digestAlgorithm = AlgorithmIdentifier.GetInstance((Asn1TaggedObject)tmp, false);
+				tmp = seq[index++];
+			}
+
+			encapsulatedContentInfo = ContentInfo.GetInstance(tmp);
+
+			tmp = seq[index++];
+			if (tmp is Asn1TaggedObject)
+			{
+				authAttrs = Asn1Set.GetInstance((Asn1TaggedObject)tmp, false);
+				tmp = seq[index++];
+			}
+
+			mac = Asn1OctetString.GetInstance(tmp);
+
+			if (seq.Count > index)
+			{
+				unauthAttrs = Asn1Set.GetInstance((Asn1TaggedObject)seq[index], false);
+			}
+		}
+
+		/**
+		 * return an AuthenticatedData object 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.
+		 * @throws ArgumentException if the object held by the
+		 *                                  tagged object cannot be converted.
+		 */
+		public static AuthenticatedData GetInstance(
+			Asn1TaggedObject	obj,
+			bool				isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+
+		/**
+		 * return an AuthenticatedData object from the given object.
+		 *
+		 * @param obj the object we want converted.
+		 * @throws ArgumentException if the object cannot be converted.
+		 */
+		public static AuthenticatedData GetInstance(
+			object	obj)
+		{
+			if (obj == null || obj is AuthenticatedData)
+			{
+				return (AuthenticatedData)obj;
+			}
+
+			if (obj is Asn1Sequence)
+			{
+				return new AuthenticatedData((Asn1Sequence)obj);
+			}
+
+			throw new ArgumentException("Invalid AuthenticatedData: " + obj.GetType().Name);
+		}
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public OriginatorInfo OriginatorInfo
+		{
+			get { return originatorInfo; }
+		}
+
+		public Asn1Set RecipientInfos
+		{
+			get { return recipientInfos; }
+		}
+
+		public AlgorithmIdentifier MacAlgorithm
+		{
+			get { return macAlgorithm; }
+		}
+
+        public AlgorithmIdentifier DigestAlgorithm
+        {
+            get { return digestAlgorithm; }
+        }
+
+		public ContentInfo EncapsulatedContentInfo
+		{
+			get { return encapsulatedContentInfo; }
+		}
+
+		public Asn1Set AuthAttrs
+		{
+			get { return authAttrs; }
+		}
+
+		public Asn1OctetString Mac
+		{
+			get { return mac; }
+		}
+
+		public Asn1Set UnauthAttrs
+		{
+			get { return unauthAttrs; }
+		}
+
+		/**
+		 * Produce an object suitable for an Asn1OutputStream.
+		 * <pre>
+		 * AuthenticatedData ::= SEQUENCE {
+		 *       version CMSVersion,
+		 *       originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+		 *       recipientInfos RecipientInfos,
+		 *       macAlgorithm MessageAuthenticationCodeAlgorithm,
+		 *       digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
+		 *       encapContentInfo EncapsulatedContentInfo,
+		 *       authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
+		 *       mac MessageAuthenticationCode,
+		 *       unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
+		 *
+		 * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
+		 *
+		 * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
+		 *
+		 * MessageAuthenticationCode ::= OCTET STRING
+		 * </pre>
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(version);
+
+			if (originatorInfo != null)
+			{
+				v.Add(new DerTaggedObject(false, 0, originatorInfo));
+			}
+
+			v.Add(recipientInfos, macAlgorithm);
+
+			if (digestAlgorithm != null)
+			{
+				v.Add(new DerTaggedObject(false, 1, digestAlgorithm));
+			}
+
+			v.Add(encapsulatedContentInfo);
+
+			if (authAttrs != null)
+			{
+				v.Add(new DerTaggedObject(false, 2, authAttrs));
+			}
+
+			v.Add(mac);
+
+			if (unauthAttrs != null)
+			{
+				v.Add(new DerTaggedObject(false, 3, unauthAttrs));
+			}
+
+			return new BerSequence(v);
+		}
+
+		public static int CalculateVersion(OriginatorInfo origInfo)
+		{
+			if (origInfo == null)
+				return 0;
+
+			int ver = 0;
+
+			foreach (object obj in origInfo.Certificates)
+			{
+				if (obj is Asn1TaggedObject)
+				{
+					Asn1TaggedObject tag = (Asn1TaggedObject)obj;
+
+					if (tag.TagNo == 2)
+					{
+						ver = 1;
+					}
+					else if (tag.TagNo == 3)
+					{
+						ver = 3;
+						break;
+					}
+				}
+			}
+
+			foreach (object obj in origInfo.Crls)
+			{
+				if (obj is Asn1TaggedObject)
+				{
+					Asn1TaggedObject tag = (Asn1TaggedObject)obj;
+
+					if (tag.TagNo == 1)
+					{
+						ver = 3;
+						break;
+					}
+				}
+			}
+
+			return ver;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/AuthenticatedDataParser.cs b/crypto/src/asn1/cms/AuthenticatedDataParser.cs
new file mode 100644
index 000000000..4b80d1b02
--- /dev/null
+++ b/crypto/src/asn1/cms/AuthenticatedDataParser.cs
@@ -0,0 +1,182 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	 * Produce an object suitable for an Asn1OutputStream.
+	 * <pre>
+	 * AuthenticatedData ::= SEQUENCE {
+	 *       version CMSVersion,
+	 *       originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+	 *       recipientInfos RecipientInfos,
+	 *       macAlgorithm MessageAuthenticationCodeAlgorithm,
+	 *       digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
+	 *       encapContentInfo EncapsulatedContentInfo,
+	 *       authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
+	 *       mac MessageAuthenticationCode,
+	 *       unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
+	 *
+	 * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
+	 *
+	 * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
+	 *
+	 * MessageAuthenticationCode ::= OCTET STRING
+	 * </pre>
+	 */
+	public class AuthenticatedDataParser
+	{
+	    private Asn1SequenceParser	seq;
+	    private DerInteger			version;
+	    private IAsn1Convertible	nextObject;
+	    private bool				originatorInfoCalled;
+
+	    public AuthenticatedDataParser(
+	        Asn1SequenceParser	seq)
+	    {
+	        this.seq = seq;
+	        this.version = (DerInteger)seq.ReadObject();
+	    }
+
+	    public DerInteger Version
+	    {
+	        get { return version; }
+	    }
+
+	    public OriginatorInfo GetOriginatorInfo()
+	    {
+	        originatorInfoCalled = true;
+
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        if (nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)nextObject).TagNo == 0)
+	        {
+	            Asn1SequenceParser originatorInfo = (Asn1SequenceParser) ((Asn1TaggedObjectParser)nextObject).GetObjectParser(Asn1Tags.Sequence, false);
+	            nextObject = null;
+	            return OriginatorInfo.GetInstance(originatorInfo.ToAsn1Object());
+	        }
+
+	        return null;
+	    }
+
+	    public Asn1SetParser GetRecipientInfos()
+	    {
+	        if (!originatorInfoCalled)
+	        {
+	            GetOriginatorInfo();
+	        }
+
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        Asn1SetParser recipientInfos = (Asn1SetParser)nextObject;
+	        nextObject = null;
+	        return recipientInfos;
+	    }
+
+	    public AlgorithmIdentifier GetMacAlgorithm()
+	    {
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        if (nextObject != null)
+	        {
+	            Asn1SequenceParser o = (Asn1SequenceParser)nextObject;
+	            nextObject = null;
+	            return AlgorithmIdentifier.GetInstance(o.ToAsn1Object());
+	        }
+
+	        return null;
+	    }
+
+		public AlgorithmIdentifier GetDigestAlgorithm()
+		{
+			if (nextObject == null)
+			{
+				nextObject = seq.ReadObject();
+			}
+
+			if (nextObject is Asn1TaggedObjectParser)
+			{
+				AlgorithmIdentifier obj = AlgorithmIdentifier.GetInstance(
+					(Asn1TaggedObject)nextObject.ToAsn1Object(), false);
+				nextObject = null;
+				return obj;
+			}
+
+			return null;
+		}
+
+	    public ContentInfoParser GetEnapsulatedContentInfo()
+	    {
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        if (nextObject != null)
+	        {
+	            Asn1SequenceParser o = (Asn1SequenceParser)nextObject;
+	            nextObject = null;
+	            return new ContentInfoParser(o);
+	        }
+
+	        return null;
+	    }
+
+	    public Asn1SetParser GetAuthAttrs()
+	    {
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        if (nextObject is Asn1TaggedObjectParser)
+	        {
+	            IAsn1Convertible o = nextObject;
+	            nextObject = null;
+	            return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false);
+	        }
+
+	        return null;
+	    }
+
+	    public Asn1OctetString GetMac()
+	    {
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        IAsn1Convertible o = nextObject;
+	        nextObject = null;
+
+	        return Asn1OctetString.GetInstance(o.ToAsn1Object());
+	    }
+
+	    public Asn1SetParser GetUnauthAttrs()
+	    {
+	        if (nextObject == null)
+	        {
+	            nextObject = seq.ReadObject();
+	        }
+
+	        if (nextObject != null)
+	        {
+	            IAsn1Convertible o = nextObject;
+	            nextObject = null;
+	            return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false);
+	        }
+
+	        return null;
+	    }
+	}
+}
diff --git a/crypto/src/asn1/cms/CMSAttributes.cs b/crypto/src/asn1/cms/CMSAttributes.cs
new file mode 100644
index 000000000..fca2b6738
--- /dev/null
+++ b/crypto/src/asn1/cms/CMSAttributes.cs
@@ -0,0 +1,14 @@
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Pkcs;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public abstract class CmsAttributes
+    {
+        public static readonly DerObjectIdentifier ContentType		= PkcsObjectIdentifiers.Pkcs9AtContentType;
+        public static readonly DerObjectIdentifier MessageDigest	= PkcsObjectIdentifiers.Pkcs9AtMessageDigest;
+        public static readonly DerObjectIdentifier SigningTime		= PkcsObjectIdentifiers.Pkcs9AtSigningTime;
+		public static readonly DerObjectIdentifier CounterSignature = PkcsObjectIdentifiers.Pkcs9AtCounterSignature;
+		public static readonly DerObjectIdentifier ContentHint		= PkcsObjectIdentifiers.IdAAContentHint;
+	}
+}
diff --git a/crypto/src/asn1/cms/CMSObjectIdentifiers.cs b/crypto/src/asn1/cms/CMSObjectIdentifiers.cs
new file mode 100644
index 000000000..2ad0a3c7c
--- /dev/null
+++ b/crypto/src/asn1/cms/CMSObjectIdentifiers.cs
@@ -0,0 +1,28 @@
+using Org.BouncyCastle.Asn1.Pkcs;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public abstract class CmsObjectIdentifiers
+    {
+        public static readonly DerObjectIdentifier Data = PkcsObjectIdentifiers.Data;
+        public static readonly DerObjectIdentifier SignedData = PkcsObjectIdentifiers.SignedData;
+        public static readonly DerObjectIdentifier EnvelopedData = PkcsObjectIdentifiers.EnvelopedData;
+        public static readonly DerObjectIdentifier SignedAndEnvelopedData = PkcsObjectIdentifiers.SignedAndEnvelopedData;
+        public static readonly DerObjectIdentifier DigestedData = PkcsObjectIdentifiers.DigestedData;
+        public static readonly DerObjectIdentifier EncryptedData = PkcsObjectIdentifiers.EncryptedData;
+        public static readonly DerObjectIdentifier AuthenticatedData = PkcsObjectIdentifiers.IdCTAuthData;
+        public static readonly DerObjectIdentifier CompressedData = PkcsObjectIdentifiers.IdCTCompressedData;
+        public static readonly DerObjectIdentifier AuthEnvelopedData = PkcsObjectIdentifiers.IdCTAuthEnvelopedData;
+        public static readonly DerObjectIdentifier timestampedData = PkcsObjectIdentifiers.IdCTTimestampedData;
+
+        /**
+         * The other Revocation Info arc
+         * id-ri OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
+         *                                   dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) }
+         */
+        public static readonly DerObjectIdentifier id_ri = new DerObjectIdentifier("1.3.6.1.5.5.7.16");
+
+        public static readonly DerObjectIdentifier id_ri_ocsp_response = id_ri.Branch("2");
+        public static readonly DerObjectIdentifier id_ri_scvp = id_ri.Branch("4");
+    }
+}
diff --git a/crypto/src/asn1/cms/CompressedData.cs b/crypto/src/asn1/cms/CompressedData.cs
new file mode 100644
index 000000000..5a2869b8c
--- /dev/null
+++ b/crypto/src/asn1/cms/CompressedData.cs
@@ -0,0 +1,96 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    /**
+     * RFC 3274 - CMS Compressed Data.
+     * <pre>
+     * CompressedData ::= Sequence {
+     *  version CMSVersion,
+     *  compressionAlgorithm CompressionAlgorithmIdentifier,
+     *  encapContentInfo EncapsulatedContentInfo
+     * }
+     * </pre>
+     */
+    public class CompressedData
+        : Asn1Encodable
+    {
+        private DerInteger			version;
+        private AlgorithmIdentifier	compressionAlgorithm;
+        private ContentInfo			encapContentInfo;
+
+		public CompressedData(
+            AlgorithmIdentifier	compressionAlgorithm,
+            ContentInfo			encapContentInfo)
+        {
+            this.version = new DerInteger(0);
+            this.compressionAlgorithm = compressionAlgorithm;
+            this.encapContentInfo = encapContentInfo;
+        }
+
+		public CompressedData(
+            Asn1Sequence seq)
+        {
+            this.version = (DerInteger) seq[0];
+            this.compressionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
+            this.encapContentInfo = ContentInfo.GetInstance(seq[2]);
+        }
+
+		/**
+         * return a CompressedData object from a tagged object.
+         *
+         * @param ato 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static CompressedData GetInstance(
+            Asn1TaggedObject ato,
+            bool explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(ato, explicitly));
+        }
+
+        /**
+         * return a CompressedData object from the given object.
+         *
+         * @param _obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static CompressedData GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is CompressedData)
+                return (CompressedData)obj;
+
+			if (obj is Asn1Sequence)
+                return new CompressedData((Asn1Sequence) obj);
+
+			throw new ArgumentException("Invalid CompressedData: " + obj.GetType().Name);
+        }
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public AlgorithmIdentifier CompressionAlgorithmIdentifier
+		{
+			get { return compressionAlgorithm; }
+		}
+
+		public ContentInfo EncapContentInfo
+		{
+			get { return encapContentInfo; }
+		}
+
+		public override Asn1Object ToAsn1Object()
+        {
+			return new BerSequence(version, compressionAlgorithm, encapContentInfo);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/CompressedDataParser.cs b/crypto/src/asn1/cms/CompressedDataParser.cs
new file mode 100644
index 000000000..7c53453df
--- /dev/null
+++ b/crypto/src/asn1/cms/CompressedDataParser.cs
@@ -0,0 +1,47 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	* RFC 3274 - CMS Compressed Data.
+	* <pre>
+	* CompressedData ::= SEQUENCE {
+	*  version CMSVersion,
+	*  compressionAlgorithm CompressionAlgorithmIdentifier,
+	*  encapContentInfo EncapsulatedContentInfo
+	* }
+	* </pre>
+	*/
+	public class CompressedDataParser
+	{
+		private DerInteger			_version;
+		private AlgorithmIdentifier	_compressionAlgorithm;
+		private ContentInfoParser	_encapContentInfo;
+
+		public CompressedDataParser(
+			Asn1SequenceParser seq)
+		{
+			this._version = (DerInteger)seq.ReadObject();
+			this._compressionAlgorithm = AlgorithmIdentifier.GetInstance(seq.ReadObject().ToAsn1Object());
+			this._encapContentInfo = new ContentInfoParser((Asn1SequenceParser)seq.ReadObject());
+		}
+
+		public DerInteger Version
+		{
+			get { return _version; }
+		}
+
+		public AlgorithmIdentifier CompressionAlgorithmIdentifier
+		{
+			get { return _compressionAlgorithm; }
+		}
+
+		public ContentInfoParser GetEncapContentInfo()
+		{
+			return _encapContentInfo;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/ContentInfo.cs b/crypto/src/asn1/cms/ContentInfo.cs
new file mode 100644
index 000000000..278ceca46
--- /dev/null
+++ b/crypto/src/asn1/cms/ContentInfo.cs
@@ -0,0 +1,88 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class ContentInfo
+        : Asn1Encodable
+    {
+        private readonly DerObjectIdentifier	contentType;
+        private readonly Asn1Encodable			content;
+
+        public static ContentInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is ContentInfo)
+                return (ContentInfo) obj;
+
+            if (obj is Asn1Sequence)
+                return new ContentInfo((Asn1Sequence) obj);
+
+            throw new ArgumentException("unknown object in factory: " + obj.GetType().Name);
+        }
+
+        public static ContentInfo GetInstance(Asn1TaggedObject obj, bool isExplicit)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+        }
+
+        private ContentInfo(
+            Asn1Sequence seq)
+        {
+            if (seq.Count < 1 || seq.Count > 2)
+                throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
+
+            contentType = (DerObjectIdentifier) seq[0];
+
+            if (seq.Count > 1)
+            {
+                Asn1TaggedObject tagged = (Asn1TaggedObject) seq[1];
+                if (!tagged.IsExplicit() || tagged.TagNo != 0)
+                    throw new ArgumentException("Bad tag for 'content'", "seq");
+
+                content = tagged.GetObject();
+            }
+        }
+
+        public ContentInfo(
+            DerObjectIdentifier	contentType,
+            Asn1Encodable		content)
+        {
+            this.contentType = contentType;
+            this.content = content;
+        }
+
+        public DerObjectIdentifier ContentType
+        {
+            get { return contentType; }
+        }
+
+        public Asn1Encodable Content
+        {
+            get { return content; }
+        }
+
+        /**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * ContentInfo ::= Sequence {
+         *          contentType ContentType,
+         *          content
+         *          [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(contentType);
+
+            if (content != null)
+            {
+                v.Add(new BerTaggedObject(0, content));
+            }
+
+            return new BerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/ContentInfoParser.cs b/crypto/src/asn1/cms/ContentInfoParser.cs
new file mode 100644
index 000000000..541cc0f59
--- /dev/null
+++ b/crypto/src/asn1/cms/ContentInfoParser.cs
@@ -0,0 +1,40 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	* Produce an object suitable for an Asn1OutputStream.
+	* <pre>
+	* ContentInfo ::= SEQUENCE {
+	*          contentType ContentType,
+	*          content
+	*          [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+	* </pre>
+	*/
+	public class ContentInfoParser
+	{
+		private DerObjectIdentifier		contentType;
+		private Asn1TaggedObjectParser	content;
+
+		public ContentInfoParser(
+			Asn1SequenceParser seq)
+		{
+			contentType = (DerObjectIdentifier)seq.ReadObject();
+			content = (Asn1TaggedObjectParser)seq.ReadObject();
+		}
+
+		public DerObjectIdentifier ContentType
+		{
+			get { return contentType; }
+		}
+
+		public IAsn1Convertible GetContent(
+			int tag)
+		{
+			if (content == null)
+				return null;
+
+			return content.GetObjectParser(tag, true);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/EncryptedContentInfo.cs b/crypto/src/asn1/cms/EncryptedContentInfo.cs
new file mode 100644
index 000000000..4fdc47138
--- /dev/null
+++ b/crypto/src/asn1/cms/EncryptedContentInfo.cs
@@ -0,0 +1,94 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class EncryptedContentInfo
+        : Asn1Encodable
+    {
+        private DerObjectIdentifier	contentType;
+        private AlgorithmIdentifier	contentEncryptionAlgorithm;
+        private Asn1OctetString		encryptedContent;
+
+		public EncryptedContentInfo(
+            DerObjectIdentifier	contentType,
+            AlgorithmIdentifier	contentEncryptionAlgorithm,
+            Asn1OctetString		encryptedContent)
+        {
+            this.contentType = contentType;
+            this.contentEncryptionAlgorithm = contentEncryptionAlgorithm;
+            this.encryptedContent = encryptedContent;
+        }
+
+		public EncryptedContentInfo(
+            Asn1Sequence seq)
+        {
+            contentType = (DerObjectIdentifier) seq[0];
+            contentEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
+
+			if (seq.Count > 2)
+            {
+                encryptedContent = Asn1OctetString.GetInstance(
+					(Asn1TaggedObject) seq[2], false);
+            }
+        }
+
+		/**
+         * return an EncryptedContentInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static EncryptedContentInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is EncryptedContentInfo)
+                return (EncryptedContentInfo)obj;
+
+			if (obj is Asn1Sequence)
+                return new EncryptedContentInfo((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid EncryptedContentInfo: " + obj.GetType().Name);
+        }
+
+        public DerObjectIdentifier ContentType
+        {
+            get { return contentType; }
+        }
+
+		public AlgorithmIdentifier ContentEncryptionAlgorithm
+        {
+			get { return contentEncryptionAlgorithm; }
+        }
+
+		public Asn1OctetString EncryptedContent
+        {
+			get { return encryptedContent; }
+        }
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * EncryptedContentInfo ::= Sequence {
+         *     contentType ContentType,
+         *     contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+         *     encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(
+				contentType, contentEncryptionAlgorithm);
+
+			if (encryptedContent != null)
+            {
+                v.Add(new BerTaggedObject(false, 0, encryptedContent));
+            }
+
+			return new BerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/EncryptedContentInfoParser.cs b/crypto/src/asn1/cms/EncryptedContentInfoParser.cs
new file mode 100644
index 000000000..af748b1b3
--- /dev/null
+++ b/crypto/src/asn1/cms/EncryptedContentInfoParser.cs
@@ -0,0 +1,46 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	* <pre>
+	* EncryptedContentInfo ::= SEQUENCE {
+	*     contentType ContentType,
+	*     contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+	*     encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL
+	* }
+	* </pre>
+	*/
+	public class EncryptedContentInfoParser
+	{
+		private DerObjectIdentifier		_contentType;
+		private AlgorithmIdentifier		_contentEncryptionAlgorithm;
+		private Asn1TaggedObjectParser	_encryptedContent;
+
+		public EncryptedContentInfoParser(
+			Asn1SequenceParser seq)
+		{
+			_contentType = (DerObjectIdentifier)seq.ReadObject();
+			_contentEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq.ReadObject().ToAsn1Object());
+			_encryptedContent = (Asn1TaggedObjectParser)seq.ReadObject();
+		}
+
+		public DerObjectIdentifier ContentType
+		{
+			get { return _contentType; }
+		}
+
+		public AlgorithmIdentifier ContentEncryptionAlgorithm
+		{
+			get { return _contentEncryptionAlgorithm; }
+		}
+
+		public IAsn1Convertible GetEncryptedContent(
+			int tag)
+		{
+			return _encryptedContent.GetObjectParser(tag, false);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/EncryptedData.cs b/crypto/src/asn1/cms/EncryptedData.cs
new file mode 100644
index 000000000..5b8378282
--- /dev/null
+++ b/crypto/src/asn1/cms/EncryptedData.cs
@@ -0,0 +1,95 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class EncryptedData
+		: Asn1Encodable
+	{
+		private readonly DerInteger				version;
+		private readonly EncryptedContentInfo	encryptedContentInfo;
+		private readonly Asn1Set				unprotectedAttrs;
+
+		public static EncryptedData GetInstance(
+			object obj)
+		{
+			if (obj is EncryptedData)
+				return (EncryptedData) obj;
+
+			if (obj is Asn1Sequence)
+				return new EncryptedData((Asn1Sequence) obj);
+
+			throw new ArgumentException("Invalid EncryptedData: " + obj.GetType().Name);
+		}
+
+		public EncryptedData(
+			EncryptedContentInfo encInfo)
+			: this(encInfo, null)
+		{
+		}
+
+		public EncryptedData(
+			EncryptedContentInfo	encInfo,
+			Asn1Set					unprotectedAttrs)
+		{
+			if (encInfo == null)
+				throw new ArgumentNullException("encInfo");
+
+			this.version = new DerInteger((unprotectedAttrs == null) ? 0 : 2);
+			this.encryptedContentInfo = encInfo;
+			this.unprotectedAttrs = unprotectedAttrs;
+		}
+
+		private EncryptedData(
+			Asn1Sequence seq)
+		{
+			if (seq == null)
+				throw new ArgumentNullException("seq");
+			if (seq.Count < 2 || seq.Count > 3)
+				throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
+
+			this.version = DerInteger.GetInstance(seq[0]);
+			this.encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[1]);
+
+			if (seq.Count > 2)
+			{
+				this.unprotectedAttrs = Asn1Set.GetInstance(seq[2]);
+			}
+		}
+
+		public virtual DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public virtual EncryptedContentInfo EncryptedContentInfo
+		{
+			get { return encryptedContentInfo; }
+		}
+
+		public virtual Asn1Set UnprotectedAttrs
+		{
+			get { return unprotectedAttrs; }
+		}
+
+		/**
+		* <pre>
+		*       EncryptedData ::= SEQUENCE {
+		*                     version CMSVersion,
+		*                     encryptedContentInfo EncryptedContentInfo,
+		*                     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
+		* </pre>
+		* @return a basic ASN.1 object representation.
+		*/
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(version, encryptedContentInfo);
+
+			if (unprotectedAttrs != null)
+			{
+				v.Add(new BerTaggedObject(false, 1, unprotectedAttrs));
+			}
+
+			return new BerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/EnvelopedData.cs b/crypto/src/asn1/cms/EnvelopedData.cs
new file mode 100644
index 000000000..09f291a93
--- /dev/null
+++ b/crypto/src/asn1/cms/EnvelopedData.cs
@@ -0,0 +1,176 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class EnvelopedData
+        : Asn1Encodable
+    {
+        private DerInteger				version;
+        private OriginatorInfo			originatorInfo;
+        private Asn1Set					recipientInfos;
+        private EncryptedContentInfo	encryptedContentInfo;
+        private Asn1Set					unprotectedAttrs;
+
+        public EnvelopedData(
+            OriginatorInfo			originatorInfo,
+            Asn1Set					recipientInfos,
+            EncryptedContentInfo	encryptedContentInfo,
+            Asn1Set					unprotectedAttrs)
+        {
+            this.version = new DerInteger(CalculateVersion(originatorInfo, recipientInfos, unprotectedAttrs));
+            this.originatorInfo = originatorInfo;
+            this.recipientInfos = recipientInfos;
+            this.encryptedContentInfo = encryptedContentInfo;
+            this.unprotectedAttrs = unprotectedAttrs;
+        }
+
+        public EnvelopedData(
+            OriginatorInfo originatorInfo,
+            Asn1Set recipientInfos,
+            EncryptedContentInfo encryptedContentInfo,
+            Attributes unprotectedAttrs)
+        {
+            this.version = new DerInteger(CalculateVersion(originatorInfo, recipientInfos, Asn1Set.GetInstance(unprotectedAttrs)));
+            this.originatorInfo = originatorInfo;
+            this.recipientInfos = recipientInfos;
+            this.encryptedContentInfo = encryptedContentInfo;
+            this.unprotectedAttrs = Asn1Set.GetInstance(unprotectedAttrs);
+        }
+
+        [Obsolete("Use 'GetInstance' instead")]
+        public EnvelopedData(
+            Asn1Sequence seq)
+        {
+            int index = 0;
+
+            version = (DerInteger) seq[index++];
+
+            object tmp = seq[index++];
+
+            if (tmp is Asn1TaggedObject)
+            {
+                originatorInfo = OriginatorInfo.GetInstance((Asn1TaggedObject) tmp, false);
+                tmp = seq[index++];
+            }
+
+            recipientInfos = Asn1Set.GetInstance(tmp);
+            encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[index++]);
+
+            if (seq.Count > index)
+            {
+                unprotectedAttrs = Asn1Set.GetInstance((Asn1TaggedObject) seq[index], false);
+            }
+        }
+
+        /**
+         * return an EnvelopedData 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static EnvelopedData GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+        /**
+         * return an EnvelopedData object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static EnvelopedData GetInstance(
+            object obj)
+        {
+            if (obj is EnvelopedData)
+                return (EnvelopedData)obj;
+            if (obj == null)
+                return null;
+            return new EnvelopedData(Asn1Sequence.GetInstance(obj));
+        }
+
+        public DerInteger Version
+        {
+            get { return version; }
+        }
+
+        public OriginatorInfo OriginatorInfo
+        {
+            get { return originatorInfo; }
+        }
+
+        public Asn1Set RecipientInfos
+        {
+            get { return recipientInfos; }
+        }
+
+        public EncryptedContentInfo EncryptedContentInfo
+        {
+            get { return encryptedContentInfo; }
+        }
+
+        public Asn1Set UnprotectedAttrs
+        {
+            get { return unprotectedAttrs; }
+        }
+
+        /**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * EnvelopedData ::= Sequence {
+         *     version CMSVersion,
+         *     originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+         *     recipientInfos RecipientInfos,
+         *     encryptedContentInfo EncryptedContentInfo,
+         *     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(version);
+
+            if (originatorInfo != null)
+            {
+                v.Add(new DerTaggedObject(false, 0, originatorInfo));
+            }
+
+            v.Add(recipientInfos, encryptedContentInfo);
+
+            if (unprotectedAttrs != null)
+            {
+                v.Add(new DerTaggedObject(false, 1, unprotectedAttrs));
+            }
+
+            return new BerSequence(v);
+        }
+
+        public static int CalculateVersion(OriginatorInfo originatorInfo, Asn1Set recipientInfos, Asn1Set unprotectedAttrs)
+        {
+            if (originatorInfo != null || unprotectedAttrs != null)
+            {
+                return 2;
+            }
+
+            foreach (object o in recipientInfos)
+            {
+                RecipientInfo ri = RecipientInfo.GetInstance(o);
+
+                if (ri.Version.Value.IntValue != 0)
+                {
+                    return 2;
+                }
+            }
+
+            return 0;
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/EnvelopedDataParser.cs b/crypto/src/asn1/cms/EnvelopedDataParser.cs
new file mode 100644
index 000000000..599353791
--- /dev/null
+++ b/crypto/src/asn1/cms/EnvelopedDataParser.cs
@@ -0,0 +1,107 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	* Produce an object suitable for an Asn1OutputStream.
+	* <pre>
+	* EnvelopedData ::= SEQUENCE {
+	*     version CMSVersion,
+	*     originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+	*     recipientInfos RecipientInfos,
+	*     encryptedContentInfo EncryptedContentInfo,
+	*     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL
+	* }
+	* </pre>
+	*/
+	public class EnvelopedDataParser
+	{
+		private Asn1SequenceParser	_seq;
+		private DerInteger			_version;
+		private IAsn1Convertible	_nextObject;
+		private bool				_originatorInfoCalled;
+
+		public EnvelopedDataParser(
+			Asn1SequenceParser seq)
+		{
+			this._seq = seq;
+			this._version = (DerInteger)seq.ReadObject();
+		}
+
+		public DerInteger Version
+		{
+			get { return _version; }
+		}
+
+		public OriginatorInfo GetOriginatorInfo() 
+		{
+			_originatorInfoCalled = true; 
+
+			if (_nextObject == null)
+			{
+				_nextObject = _seq.ReadObject();
+			}
+
+			if (_nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)_nextObject).TagNo == 0)
+			{
+				Asn1SequenceParser originatorInfo = (Asn1SequenceParser)
+					((Asn1TaggedObjectParser)_nextObject).GetObjectParser(Asn1Tags.Sequence, false);
+				_nextObject = null;
+				return OriginatorInfo.GetInstance(originatorInfo.ToAsn1Object());
+			}
+
+			return null;
+		}
+
+		public Asn1SetParser GetRecipientInfos()
+		{
+			if (!_originatorInfoCalled)
+			{
+				GetOriginatorInfo();
+			}
+
+			if (_nextObject == null)
+			{
+				_nextObject = _seq.ReadObject();
+			}
+
+			Asn1SetParser recipientInfos = (Asn1SetParser)_nextObject;
+			_nextObject = null;
+			return recipientInfos;
+		}
+
+		public EncryptedContentInfoParser GetEncryptedContentInfo()
+		{
+			if (_nextObject == null)
+			{
+				_nextObject = _seq.ReadObject();
+			}
+
+			if (_nextObject != null)
+			{
+				Asn1SequenceParser o = (Asn1SequenceParser) _nextObject;
+				_nextObject = null;
+				return new EncryptedContentInfoParser(o);
+			}
+
+			return null;
+		}
+
+		public Asn1SetParser GetUnprotectedAttrs()
+		{
+			if (_nextObject == null)
+			{
+				_nextObject = _seq.ReadObject();
+			}
+
+			if (_nextObject != null)
+			{
+				IAsn1Convertible o = _nextObject;
+				_nextObject = null;
+				return (Asn1SetParser)((Asn1TaggedObjectParser)o).GetObjectParser(Asn1Tags.Set, false);
+			}
+        
+			return null;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/Evidence.cs b/crypto/src/asn1/cms/Evidence.cs
new file mode 100644
index 000000000..4745e565b
--- /dev/null
+++ b/crypto/src/asn1/cms/Evidence.cs
@@ -0,0 +1,47 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class Evidence
+		: Asn1Encodable, IAsn1Choice
+	{
+		private TimeStampTokenEvidence tstEvidence;
+
+		public Evidence(TimeStampTokenEvidence tstEvidence)
+		{
+			this.tstEvidence = tstEvidence;
+		}
+
+		private Evidence(Asn1TaggedObject tagged)
+		{
+			if (tagged.TagNo == 0)
+			{
+				this.tstEvidence = TimeStampTokenEvidence.GetInstance(tagged, false);
+			}
+		}
+
+		public static Evidence GetInstance(object obj)
+		{
+			if (obj is Evidence)
+				return (Evidence)obj;
+
+			if (obj is Asn1TaggedObject)
+				return new Evidence(Asn1TaggedObject.GetInstance(obj));
+
+			throw new ArgumentException("Unknown object in GetInstance: " + obj.GetType().FullName, "obj");
+		}
+
+		public virtual TimeStampTokenEvidence TstEvidence
+		{
+			get { return tstEvidence; }
+		}
+
+		public override Asn1Object ToAsn1Object()
+		{
+			if (tstEvidence != null)
+				return new DerTaggedObject(false, 0, tstEvidence);
+
+			return null;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/IssuerAndSerialNumber.cs b/crypto/src/asn1/cms/IssuerAndSerialNumber.cs
new file mode 100644
index 000000000..b509e7e19
--- /dev/null
+++ b/crypto/src/asn1/cms/IssuerAndSerialNumber.cs
@@ -0,0 +1,64 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Math;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class IssuerAndSerialNumber
+        : Asn1Encodable
+    {
+        private X509Name	name;
+        private DerInteger	serialNumber;
+
+        public static IssuerAndSerialNumber GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+            IssuerAndSerialNumber existing = obj as IssuerAndSerialNumber;
+            if (existing != null)
+                return existing;
+            return new IssuerAndSerialNumber(Asn1Sequence.GetInstance(obj));
+        }
+
+        [Obsolete("Use GetInstance() instead")]
+        public IssuerAndSerialNumber(
+            Asn1Sequence seq)
+        {
+            this.name = X509Name.GetInstance(seq[0]);
+            this.serialNumber = (DerInteger) seq[1];
+        }
+
+        public IssuerAndSerialNumber(
+            X509Name	name,
+            BigInteger	serialNumber)
+        {
+            this.name = name;
+            this.serialNumber = new DerInteger(serialNumber);
+        }
+
+        public IssuerAndSerialNumber(
+            X509Name	name,
+            DerInteger	serialNumber)
+        {
+            this.name = name;
+            this.serialNumber = serialNumber;
+        }
+
+        public X509Name Name
+        {
+            get { return name; }
+        }
+
+        public DerInteger SerialNumber
+        {
+            get { return serialNumber; }
+        }
+
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerSequence(name, serialNumber);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/KEKIdentifier.cs b/crypto/src/asn1/cms/KEKIdentifier.cs
new file mode 100644
index 000000000..e5d1d9090
--- /dev/null
+++ b/crypto/src/asn1/cms/KEKIdentifier.cs
@@ -0,0 +1,119 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class KekIdentifier
+        : Asn1Encodable
+    {
+        private Asn1OctetString		keyIdentifier;
+        private DerGeneralizedTime	date;
+        private OtherKeyAttribute	other;
+
+		public KekIdentifier(
+            byte[]              keyIdentifier,
+            DerGeneralizedTime  date,
+            OtherKeyAttribute   other)
+        {
+            this.keyIdentifier = new DerOctetString(keyIdentifier);
+            this.date = date;
+            this.other = other;
+        }
+
+		public KekIdentifier(
+            Asn1Sequence seq)
+        {
+            keyIdentifier = (Asn1OctetString) seq[0];
+
+			switch (seq.Count)
+            {
+            case 1:
+				break;
+            case 2:
+				if (seq[1] is DerGeneralizedTime)
+				{
+					date = (DerGeneralizedTime) seq[1];
+				}
+				else
+				{
+					other = OtherKeyAttribute.GetInstance(seq[2]);
+				}
+				break;
+            case 3:
+				date  = (DerGeneralizedTime) seq[1];
+				other = OtherKeyAttribute.GetInstance(seq[2]);
+				break;
+            default:
+				throw new ArgumentException("Invalid KekIdentifier");
+            }
+        }
+
+		/**
+         * return a KekIdentifier 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static KekIdentifier GetInstance(
+            Asn1TaggedObject obj,
+            bool explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+        /**
+         * return a KekIdentifier object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static KekIdentifier GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is KekIdentifier)
+                return (KekIdentifier)obj;
+
+			if (obj is Asn1Sequence)
+                return new KekIdentifier((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid KekIdentifier: " + obj.GetType().Name);
+        }
+
+		public Asn1OctetString KeyIdentifier
+		{
+			get { return keyIdentifier; }
+		}
+
+		public DerGeneralizedTime Date
+		{
+			get { return date; }
+		}
+
+		public OtherKeyAttribute Other
+		{
+			get { return other; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * KekIdentifier ::= Sequence {
+         *     keyIdentifier OCTET STRING,
+         *     date GeneralizedTime OPTIONAL,
+         *     other OtherKeyAttribute OPTIONAL
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(keyIdentifier);
+			v.AddOptional(date, other);
+			return new DerSequence(v);
+        }
+    }
+}
+
diff --git a/crypto/src/asn1/cms/KEKRecipientInfo.cs b/crypto/src/asn1/cms/KEKRecipientInfo.cs
new file mode 100644
index 000000000..d847b50cc
--- /dev/null
+++ b/crypto/src/asn1/cms/KEKRecipientInfo.cs
@@ -0,0 +1,106 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class KekRecipientInfo
+        : Asn1Encodable
+    {
+        private DerInteger			version;
+        private KekIdentifier       kekID;
+        private AlgorithmIdentifier keyEncryptionAlgorithm;
+        private Asn1OctetString     encryptedKey;
+
+		public KekRecipientInfo(
+            KekIdentifier       kekID,
+            AlgorithmIdentifier keyEncryptionAlgorithm,
+            Asn1OctetString     encryptedKey)
+        {
+            this.version = new DerInteger(4);
+            this.kekID = kekID;
+            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+            this.encryptedKey = encryptedKey;
+        }
+
+		public KekRecipientInfo(
+            Asn1Sequence seq)
+        {
+            version = (DerInteger) seq[0];
+            kekID = KekIdentifier.GetInstance(seq[1]);
+            keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
+            encryptedKey = (Asn1OctetString) seq[3];
+        }
+
+		/**
+         * return a KekRecipientInfo 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static KekRecipientInfo GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+        /**
+         * return a KekRecipientInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static KekRecipientInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is KekRecipientInfo)
+                return (KekRecipientInfo)obj;
+
+			if(obj is Asn1Sequence)
+                return new KekRecipientInfo((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid KekRecipientInfo: " + obj.GetType().Name);
+        }
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public KekIdentifier KekID
+		{
+			get { return kekID; }
+		}
+
+		public AlgorithmIdentifier KeyEncryptionAlgorithm
+		{
+			get { return keyEncryptionAlgorithm; }
+		}
+
+		public Asn1OctetString EncryptedKey
+		{
+			get { return encryptedKey; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * KekRecipientInfo ::= Sequence {
+         *     version CMSVersion,  -- always set to 4
+         *     kekID KekIdentifier,
+         *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+         *     encryptedKey EncryptedKey
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+			return new DerSequence(version, kekID, keyEncryptionAlgorithm, encryptedKey);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs b/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs
new file mode 100644
index 000000000..fa6fdb0f3
--- /dev/null
+++ b/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs
@@ -0,0 +1,92 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class KeyAgreeRecipientIdentifier
+		: Asn1Encodable, IAsn1Choice
+	{
+		/**
+		 * return an KeyAgreeRecipientIdentifier object 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 object held by the
+		 *          tagged object cannot be converted.
+		 */
+		public static KeyAgreeRecipientIdentifier GetInstance(
+			Asn1TaggedObject	obj,
+			bool				isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+    
+		/**
+		 * return an KeyAgreeRecipientIdentifier object from the given object.
+		 *
+		 * @param obj the object we want converted.
+		 * @exception ArgumentException if the object cannot be converted.
+		 */
+		public static KeyAgreeRecipientIdentifier GetInstance(
+			object obj)
+		{
+			if (obj == null || obj is KeyAgreeRecipientIdentifier)
+				return (KeyAgreeRecipientIdentifier)obj;
+
+			if (obj is Asn1Sequence)
+				return new KeyAgreeRecipientIdentifier(IssuerAndSerialNumber.GetInstance(obj));
+
+			if (obj is Asn1TaggedObject && ((Asn1TaggedObject)obj).TagNo == 0)
+			{
+				return new KeyAgreeRecipientIdentifier(RecipientKeyIdentifier.GetInstance(
+					(Asn1TaggedObject)obj, false));
+			}
+
+			throw new ArgumentException("Invalid KeyAgreeRecipientIdentifier: " + obj.GetType().FullName, "obj");
+		} 
+
+		private readonly IssuerAndSerialNumber issuerSerial;
+		private readonly RecipientKeyIdentifier rKeyID;
+
+		public KeyAgreeRecipientIdentifier(
+			IssuerAndSerialNumber issuerSerial)
+		{
+			this.issuerSerial = issuerSerial;
+		}
+
+		public KeyAgreeRecipientIdentifier(
+			RecipientKeyIdentifier rKeyID)
+		{
+			this.rKeyID = rKeyID;
+		}
+
+		public IssuerAndSerialNumber IssuerAndSerialNumber
+		{
+			get { return issuerSerial; }
+		}
+
+		public RecipientKeyIdentifier RKeyID
+		{
+			get { return rKeyID; }
+		}
+
+		/** 
+		 * Produce an object suitable for an Asn1OutputStream.
+		 * <pre>
+		 * KeyAgreeRecipientIdentifier ::= CHOICE {
+		 *     issuerAndSerialNumber IssuerAndSerialNumber,
+		 *     rKeyId [0] IMPLICIT RecipientKeyIdentifier
+		 * }
+		 * </pre>
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			if (issuerSerial != null)
+			{
+				return issuerSerial.ToAsn1Object();
+			}
+
+			return new DerTaggedObject(false, 0, rKeyID);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs b/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs
new file mode 100644
index 000000000..aafb008d4
--- /dev/null
+++ b/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs
@@ -0,0 +1,141 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class KeyAgreeRecipientInfo
+        : Asn1Encodable
+    {
+        private DerInteger                  version;
+        private OriginatorIdentifierOrKey   originator;
+        private Asn1OctetString             ukm;
+        private AlgorithmIdentifier         keyEncryptionAlgorithm;
+        private Asn1Sequence                recipientEncryptedKeys;
+
+		public KeyAgreeRecipientInfo(
+            OriginatorIdentifierOrKey   originator,
+            Asn1OctetString             ukm,
+            AlgorithmIdentifier         keyEncryptionAlgorithm,
+            Asn1Sequence                recipientEncryptedKeys)
+        {
+            this.version = new DerInteger(3);
+            this.originator = originator;
+            this.ukm = ukm;
+            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+            this.recipientEncryptedKeys = recipientEncryptedKeys;
+        }
+
+		public KeyAgreeRecipientInfo(
+            Asn1Sequence seq)
+        {
+            int index = 0;
+
+            version = (DerInteger) seq[index++];
+            originator = OriginatorIdentifierOrKey.GetInstance(
+				(Asn1TaggedObject) seq[index++], true);
+
+			if (seq[index] is Asn1TaggedObject)
+            {
+                ukm = Asn1OctetString.GetInstance(
+					(Asn1TaggedObject) seq[index++], true);
+            }
+
+			keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(
+				seq[index++]);
+
+			recipientEncryptedKeys = (Asn1Sequence) seq[index++];
+        }
+
+		/**
+         * return a KeyAgreeRecipientInfo 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static KeyAgreeRecipientInfo GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+		/**
+         * return a KeyAgreeRecipientInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static KeyAgreeRecipientInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is KeyAgreeRecipientInfo)
+                return (KeyAgreeRecipientInfo)obj;
+
+			if (obj is Asn1Sequence)
+                return new KeyAgreeRecipientInfo((Asn1Sequence)obj);
+
+			throw new ArgumentException(
+                "Illegal object in KeyAgreeRecipientInfo: " + obj.GetType().Name);
+
+        }
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public OriginatorIdentifierOrKey Originator
+		{
+			get { return originator; }
+		}
+
+		public Asn1OctetString UserKeyingMaterial
+		{
+			get { return ukm; }
+		}
+
+		public AlgorithmIdentifier KeyEncryptionAlgorithm
+		{
+			get { return keyEncryptionAlgorithm; }
+		}
+
+		public Asn1Sequence RecipientEncryptedKeys
+		{
+			get { return recipientEncryptedKeys; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * KeyAgreeRecipientInfo ::= Sequence {
+         *     version CMSVersion,  -- always set to 3
+         *     originator [0] EXPLICIT OriginatorIdentifierOrKey,
+         *     ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
+         *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+         *     recipientEncryptedKeys RecipientEncryptedKeys
+         * }
+		 *
+		 * UserKeyingMaterial ::= OCTET STRING
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(
+				version, new DerTaggedObject(true, 0, originator));
+
+			if (ukm != null)
+            {
+                v.Add(new DerTaggedObject(true, 1, ukm));
+            }
+
+			v.Add(keyEncryptionAlgorithm, recipientEncryptedKeys);
+
+			return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/KeyTransRecipientInfo.cs b/crypto/src/asn1/cms/KeyTransRecipientInfo.cs
new file mode 100644
index 000000000..aae18c59d
--- /dev/null
+++ b/crypto/src/asn1/cms/KeyTransRecipientInfo.cs
@@ -0,0 +1,99 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class KeyTransRecipientInfo
+        : Asn1Encodable
+    {
+        private DerInteger          version;
+        private RecipientIdentifier rid;
+        private AlgorithmIdentifier keyEncryptionAlgorithm;
+        private Asn1OctetString     encryptedKey;
+
+		public KeyTransRecipientInfo(
+            RecipientIdentifier rid,
+            AlgorithmIdentifier keyEncryptionAlgorithm,
+            Asn1OctetString     encryptedKey)
+        {
+            if (rid.ToAsn1Object() is Asn1TaggedObject)
+            {
+                this.version = new DerInteger(2);
+            }
+            else
+            {
+                this.version = new DerInteger(0);
+            }
+
+			this.rid = rid;
+            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+            this.encryptedKey = encryptedKey;
+        }
+
+		public KeyTransRecipientInfo(
+            Asn1Sequence seq)
+        {
+            this.version = (DerInteger) seq[0];
+            this.rid = RecipientIdentifier.GetInstance(seq[1]);
+            this.keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
+            this.encryptedKey = (Asn1OctetString) seq[3];
+        }
+
+		/**
+         * return a KeyTransRecipientInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static KeyTransRecipientInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is KeyTransRecipientInfo)
+                return (KeyTransRecipientInfo) obj;
+
+			if(obj is Asn1Sequence)
+                return new KeyTransRecipientInfo((Asn1Sequence) obj);
+
+			throw new ArgumentException(
+				"Illegal object in KeyTransRecipientInfo: " + obj.GetType().Name);
+        }
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public RecipientIdentifier RecipientIdentifier
+		{
+			get { return rid; }
+		}
+
+		public AlgorithmIdentifier KeyEncryptionAlgorithm
+		{
+			get { return keyEncryptionAlgorithm; }
+		}
+
+		public Asn1OctetString EncryptedKey
+		{
+			get { return encryptedKey; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * KeyTransRecipientInfo ::= Sequence {
+         *     version CMSVersion,  -- always set to 0 or 2
+         *     rid RecipientIdentifier,
+         *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+         *     encryptedKey EncryptedKey
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+			return new DerSequence(version, rid, keyEncryptionAlgorithm, encryptedKey);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/MetaData.cs b/crypto/src/asn1/cms/MetaData.cs
new file mode 100644
index 000000000..ad2b5c426
--- /dev/null
+++ b/crypto/src/asn1/cms/MetaData.cs
@@ -0,0 +1,94 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class MetaData
+		: Asn1Encodable
+	{
+		private DerBoolean hashProtected;
+		private DerUtf8String fileName;
+		private DerIA5String  mediaType;
+		private Attributes otherMetaData;
+
+		public MetaData(
+			DerBoolean		hashProtected,
+			DerUtf8String	fileName,
+			DerIA5String	mediaType,
+			Attributes		otherMetaData)
+		{
+			this.hashProtected = hashProtected;
+			this.fileName = fileName;
+			this.mediaType = mediaType;
+			this.otherMetaData = otherMetaData;
+		}
+
+		private MetaData(Asn1Sequence seq)
+		{
+			this.hashProtected = DerBoolean.GetInstance(seq[0]);
+
+			int index = 1;
+
+			if (index < seq.Count && seq[index] is DerUtf8String)
+			{
+				this.fileName = DerUtf8String.GetInstance(seq[index++]);
+			}
+			if (index < seq.Count && seq[index] is DerIA5String)
+			{
+				this.mediaType = DerIA5String.GetInstance(seq[index++]);
+			}
+			if (index < seq.Count)
+			{
+				this.otherMetaData = Attributes.GetInstance(seq[index++]);
+			}
+		}
+
+		public static MetaData GetInstance(object obj)
+		{
+			if (obj is MetaData)
+				return (MetaData)obj;
+
+			if (obj != null)
+				return new MetaData(Asn1Sequence.GetInstance(obj));
+
+			return null;
+		}
+
+		/**
+		 * <pre>
+		 * MetaData ::= SEQUENCE {
+		 *   hashProtected        BOOLEAN,
+		 *   fileName             UTF8String OPTIONAL,
+		 *   mediaType            IA5String OPTIONAL,
+		 *   otherMetaData        Attributes OPTIONAL
+		 * }
+		 * </pre>
+		 * @return
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(hashProtected);
+			v.AddOptional(fileName, mediaType, otherMetaData);
+			return new DerSequence(v);
+		}
+
+		public virtual bool IsHashProtected
+		{
+			get { return hashProtected.IsTrue; }
+		}
+
+		public virtual DerUtf8String FileName
+		{
+			get { return fileName; }
+		}
+
+		public virtual DerIA5String MediaType
+		{
+			get { return mediaType; }
+		}
+
+		public virtual Attributes OtherMetaData
+		{
+			get { return otherMetaData; }
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs b/crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs
new file mode 100644
index 000000000..d33a11725
--- /dev/null
+++ b/crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs
@@ -0,0 +1,168 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class OriginatorIdentifierOrKey
+        : Asn1Encodable, IAsn1Choice
+    {
+        private Asn1Encodable id;
+
+        public OriginatorIdentifierOrKey(
+            IssuerAndSerialNumber id)
+        {
+            this.id = id;
+        }
+
+		[Obsolete("Use version taking a 'SubjectKeyIdentifier'")]
+        public OriginatorIdentifierOrKey(
+            Asn1OctetString id)
+			: this(new SubjectKeyIdentifier(id))
+        {
+        }
+
+        public OriginatorIdentifierOrKey(
+            SubjectKeyIdentifier id)
+        {
+            this.id = new DerTaggedObject(false, 0, id);
+        }
+
+        public OriginatorIdentifierOrKey(
+            OriginatorPublicKey id)
+        {
+            this.id = new DerTaggedObject(false, 1, id);
+        }
+
+		[Obsolete("Use more specific version")]
+        public OriginatorIdentifierOrKey(
+            Asn1Object id)
+        {
+            this.id = id;
+        }
+
+		private OriginatorIdentifierOrKey(
+			Asn1TaggedObject id)
+		{
+			// TODO Add validation
+			this.id = id;
+		}
+
+		/**
+         * return an OriginatorIdentifierOrKey object from a tagged object.
+         *
+         * @param o 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static OriginatorIdentifierOrKey GetInstance(
+            Asn1TaggedObject	o,
+            bool				explicitly)
+        {
+            if (!explicitly)
+            {
+                throw new ArgumentException(
+                        "Can't implicitly tag OriginatorIdentifierOrKey");
+            }
+
+			return GetInstance(o.GetObject());
+        }
+
+        /**
+         * return an OriginatorIdentifierOrKey object from the given object.
+         *
+         * @param o the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static OriginatorIdentifierOrKey GetInstance(
+            object o)
+        {
+            if (o == null || o is OriginatorIdentifierOrKey)
+                return (OriginatorIdentifierOrKey)o;
+
+			if (o is IssuerAndSerialNumber)
+				return new OriginatorIdentifierOrKey((IssuerAndSerialNumber)o);
+
+			if (o is SubjectKeyIdentifier)
+				return new OriginatorIdentifierOrKey((SubjectKeyIdentifier)o);
+
+			if (o is OriginatorPublicKey)
+				return new OriginatorIdentifierOrKey((OriginatorPublicKey)o);
+
+			if (o is Asn1TaggedObject)
+				return new OriginatorIdentifierOrKey((Asn1TaggedObject)o);
+
+			throw new ArgumentException("Invalid OriginatorIdentifierOrKey: " + o.GetType().Name);
+        }
+
+		public Asn1Encodable ID
+		{
+			get { return id; }
+		}
+
+		public IssuerAndSerialNumber IssuerAndSerialNumber
+		{
+			get
+			{
+				if (id is IssuerAndSerialNumber)
+				{
+					return (IssuerAndSerialNumber)id;
+				}
+
+				return null;
+			}
+		}
+
+		public SubjectKeyIdentifier SubjectKeyIdentifier
+		{
+			get
+			{
+				if (id is Asn1TaggedObject && ((Asn1TaggedObject)id).TagNo == 0)
+				{
+					return SubjectKeyIdentifier.GetInstance((Asn1TaggedObject)id, false);
+				}
+
+				return null;
+			}
+		}
+
+		[Obsolete("Use 'OriginatorPublicKey' property")]
+		public OriginatorPublicKey OriginatorKey
+		{
+			get { return OriginatorPublicKey; }
+		}
+
+		public OriginatorPublicKey OriginatorPublicKey
+		{
+			get
+			{
+				if (id is Asn1TaggedObject && ((Asn1TaggedObject)id).TagNo == 1)
+				{
+					return OriginatorPublicKey.GetInstance((Asn1TaggedObject)id, false);
+				}
+
+				return null;
+			}
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * OriginatorIdentifierOrKey ::= CHOICE {
+         *     issuerAndSerialNumber IssuerAndSerialNumber,
+         *     subjectKeyIdentifier [0] SubjectKeyIdentifier,
+         *     originatorKey [1] OriginatorPublicKey
+         * }
+         *
+         * SubjectKeyIdentifier ::= OCTET STRING
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return id.ToAsn1Object();
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/OriginatorInfo.cs b/crypto/src/asn1/cms/OriginatorInfo.cs
new file mode 100644
index 000000000..b4549bc36
--- /dev/null
+++ b/crypto/src/asn1/cms/OriginatorInfo.cs
@@ -0,0 +1,121 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class OriginatorInfo
+        : Asn1Encodable
+    {
+        private Asn1Set certs;
+        private Asn1Set crls;
+
+        public OriginatorInfo(
+            Asn1Set certs,
+            Asn1Set crls)
+        {
+            this.certs = certs;
+            this.crls = crls;
+        }
+
+		public OriginatorInfo(
+            Asn1Sequence seq)
+        {
+            switch (seq.Count)
+            {
+            case 0:     // empty
+                break;
+            case 1:
+                Asn1TaggedObject o = (Asn1TaggedObject) seq[0];
+                switch (o.TagNo)
+                {
+                case 0 :
+                    certs = Asn1Set.GetInstance(o, false);
+                    break;
+                case 1 :
+                    crls = Asn1Set.GetInstance(o, false);
+                    break;
+                default:
+                    throw new ArgumentException("Bad tag in OriginatorInfo: " + o.TagNo);
+                }
+                break;
+            case 2:
+                certs = Asn1Set.GetInstance((Asn1TaggedObject) seq[0], false);
+                crls  = Asn1Set.GetInstance((Asn1TaggedObject) seq[1], false);
+                break;
+            default:
+                throw new ArgumentException("OriginatorInfo too big");
+            }
+        }
+
+		/**
+         * return an OriginatorInfo 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static OriginatorInfo GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+		/**
+         * return an OriginatorInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static OriginatorInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is OriginatorInfo)
+                return (OriginatorInfo)obj;
+
+			if (obj is Asn1Sequence)
+                return new OriginatorInfo((Asn1Sequence)obj);
+
+			throw new ArgumentException("Invalid OriginatorInfo: " + obj.GetType().Name);
+        }
+
+		public Asn1Set Certificates
+		{
+			get { return certs; }
+		}
+
+		public Asn1Set Crls
+		{
+			get { return crls; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * OriginatorInfo ::= Sequence {
+         *     certs [0] IMPLICIT CertificateSet OPTIONAL,
+         *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector();
+
+			if (certs != null)
+            {
+                v.Add(new DerTaggedObject(false, 0, certs));
+            }
+
+			if (crls != null)
+            {
+                v.Add(new DerTaggedObject(false, 1, crls));
+            }
+
+			return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/OriginatorPublicKey.cs b/crypto/src/asn1/cms/OriginatorPublicKey.cs
new file mode 100644
index 000000000..aabaf4386
--- /dev/null
+++ b/crypto/src/asn1/cms/OriginatorPublicKey.cs
@@ -0,0 +1,87 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class OriginatorPublicKey
+        : Asn1Encodable
+    {
+        private AlgorithmIdentifier algorithm;
+        private DerBitString        publicKey;
+
+		public OriginatorPublicKey(
+            AlgorithmIdentifier algorithm,
+            byte[]              publicKey)
+        {
+            this.algorithm = algorithm;
+            this.publicKey = new DerBitString(publicKey);
+        }
+
+		public OriginatorPublicKey(
+            Asn1Sequence seq)
+        {
+            algorithm = AlgorithmIdentifier.GetInstance(seq[0]);
+            publicKey = (DerBitString) seq[1];
+        }
+
+		/**
+         * return an OriginatorPublicKey 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static OriginatorPublicKey GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+		/**
+         * return an OriginatorPublicKey object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static OriginatorPublicKey GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is OriginatorPublicKey)
+                return (OriginatorPublicKey)obj;
+
+			if (obj is Asn1Sequence)
+                return new OriginatorPublicKey((Asn1Sequence) obj);
+
+			throw new ArgumentException("Invalid OriginatorPublicKey: " + obj.GetType().Name);
+        }
+
+		public AlgorithmIdentifier Algorithm
+		{
+			get { return algorithm; }
+		}
+
+		public DerBitString PublicKey
+		{
+			get { return publicKey; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * OriginatorPublicKey ::= Sequence {
+         *     algorithm AlgorithmIdentifier,
+         *     publicKey BIT STRING
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+			return new DerSequence(algorithm, publicKey);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/OtherKeyAttribute.cs b/crypto/src/asn1/cms/OtherKeyAttribute.cs
new file mode 100644
index 000000000..271059175
--- /dev/null
+++ b/crypto/src/asn1/cms/OtherKeyAttribute.cs
@@ -0,0 +1,70 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class OtherKeyAttribute
+        : Asn1Encodable
+    {
+        private DerObjectIdentifier	keyAttrId;
+        private Asn1Encodable		keyAttr;
+
+		/**
+         * return an OtherKeyAttribute object from the given object.
+         *
+         * @param o the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static OtherKeyAttribute GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is OtherKeyAttribute)
+                return (OtherKeyAttribute) obj;
+
+			if (obj is Asn1Sequence)
+                return new OtherKeyAttribute((Asn1Sequence) obj);
+
+			throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj");
+        }
+
+		public OtherKeyAttribute(
+            Asn1Sequence seq)
+        {
+            keyAttrId = (DerObjectIdentifier) seq[0];
+            keyAttr = seq[1];
+        }
+
+		public OtherKeyAttribute(
+            DerObjectIdentifier	keyAttrId,
+            Asn1Encodable		keyAttr)
+        {
+            this.keyAttrId = keyAttrId;
+            this.keyAttr = keyAttr;
+        }
+
+		public DerObjectIdentifier KeyAttrId
+		{
+			get { return keyAttrId; }
+		}
+
+		public Asn1Encodable KeyAttr
+		{
+			get { return keyAttr; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * OtherKeyAttribute ::= Sequence {
+         *     keyAttrId OBJECT IDENTIFIER,
+         *     keyAttr ANY DEFINED BY keyAttrId OPTIONAL
+         * }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+			return new DerSequence(keyAttrId, keyAttr);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/OtherRecipientInfo.cs b/crypto/src/asn1/cms/OtherRecipientInfo.cs
new file mode 100644
index 000000000..80dd68e7c
--- /dev/null
+++ b/crypto/src/asn1/cms/OtherRecipientInfo.cs
@@ -0,0 +1,83 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class OtherRecipientInfo
+        : Asn1Encodable
+    {
+        private readonly DerObjectIdentifier oriType;
+        private readonly Asn1Encodable oriValue;
+
+        public OtherRecipientInfo(
+            DerObjectIdentifier	oriType,
+            Asn1Encodable		oriValue)
+        {
+            this.oriType = oriType;
+            this.oriValue = oriValue;
+        }
+
+        [Obsolete("Use GetInstance() instead")]
+        public OtherRecipientInfo(
+            Asn1Sequence seq)
+        {
+            oriType = DerObjectIdentifier.GetInstance(seq[0]);
+            oriValue = seq[1];
+        }
+
+        /**
+         * return a OtherRecipientInfo 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static OtherRecipientInfo GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+        /**
+         * return a OtherRecipientInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static OtherRecipientInfo GetInstance(
+            object obj)
+        {
+            if (obj == null)
+                return null;
+            OtherRecipientInfo existing = obj as OtherRecipientInfo;
+            if (existing != null)
+                return existing;
+            return new OtherRecipientInfo(Asn1Sequence.GetInstance(obj));
+        }
+
+        public virtual DerObjectIdentifier OriType
+        {
+            get { return oriType; }
+        }
+
+        public virtual Asn1Encodable OriValue
+        {
+            get { return oriValue; }
+        }
+
+        /**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * OtherRecipientInfo ::= Sequence {
+         *    oriType OBJECT IDENTIFIER,
+         *    oriValue ANY DEFINED BY oriType }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerSequence(oriType, oriValue);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
new file mode 100644
index 000000000..78354896f
--- /dev/null
+++ b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
@@ -0,0 +1,77 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class OtherRevocationInfoFormat
+        : Asn1Encodable
+    {
+        private readonly DerObjectIdentifier otherRevInfoFormat;
+        private readonly Asn1Encodable otherRevInfo;
+
+        public OtherRevocationInfoFormat(
+            DerObjectIdentifier otherRevInfoFormat,
+            Asn1Encodable otherRevInfo)
+        {
+            this.otherRevInfoFormat = otherRevInfoFormat;
+            this.otherRevInfo = otherRevInfo;
+        }
+
+        private OtherRevocationInfoFormat(Asn1Sequence seq)
+        {
+            otherRevInfoFormat = DerObjectIdentifier.GetInstance(seq[0]);
+            otherRevInfo = seq[1];
+        }
+
+        /**
+         * return a OtherRevocationInfoFormat object 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static OtherRevocationInfoFormat GetInstance(Asn1TaggedObject obj, bool isExplicit)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+        }
+
+        /**
+         * return a OtherRevocationInfoFormat object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception IllegalArgumentException if the object cannot be converted.
+         */
+        public static OtherRevocationInfoFormat GetInstance(object obj)
+        {
+            if (obj is OtherRevocationInfoFormat)
+                return (OtherRevocationInfoFormat)obj;
+            if (obj != null)
+                return new OtherRevocationInfoFormat(Asn1Sequence.GetInstance(obj));
+            return null;
+        }
+
+        public virtual DerObjectIdentifier InfoFormat
+        {
+            get { return otherRevInfoFormat; }
+        }
+
+        public virtual Asn1Encodable Info
+        {
+            get { return otherRevInfo; }
+        }
+
+        /** 
+         * Produce an object suitable for an ASN1OutputStream.
+         * <pre>
+         * OtherRevocationInfoFormat ::= SEQUENCE {
+         *      otherRevInfoFormat OBJECT IDENTIFIER,
+         *      otherRevInfo ANY DEFINED BY otherRevInfoFormat }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerSequence(otherRevInfoFormat, otherRevInfo);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/PasswordRecipientInfo.cs b/crypto/src/asn1/cms/PasswordRecipientInfo.cs
new file mode 100644
index 000000000..800b57951
--- /dev/null
+++ b/crypto/src/asn1/cms/PasswordRecipientInfo.cs
@@ -0,0 +1,133 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class PasswordRecipientInfo
+        : Asn1Encodable
+    {
+        private readonly DerInteger				version;
+        private readonly AlgorithmIdentifier	keyDerivationAlgorithm;
+        private readonly AlgorithmIdentifier	keyEncryptionAlgorithm;
+        private readonly Asn1OctetString		encryptedKey;
+
+		public PasswordRecipientInfo(
+            AlgorithmIdentifier	keyEncryptionAlgorithm,
+            Asn1OctetString		encryptedKey)
+        {
+            this.version = new DerInteger(0);
+            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+            this.encryptedKey = encryptedKey;
+        }
+
+		public PasswordRecipientInfo(
+			AlgorithmIdentifier	keyDerivationAlgorithm,
+			AlgorithmIdentifier	keyEncryptionAlgorithm,
+			Asn1OctetString		encryptedKey)
+		{
+			this.version = new DerInteger(0);
+			this.keyDerivationAlgorithm = keyDerivationAlgorithm;
+			this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
+			this.encryptedKey = encryptedKey;
+		}
+
+		public PasswordRecipientInfo(
+            Asn1Sequence seq)
+        {
+            version = (DerInteger) seq[0];
+
+			if (seq[1] is Asn1TaggedObject)
+            {
+                keyDerivationAlgorithm = AlgorithmIdentifier.GetInstance((Asn1TaggedObject) seq[1], false);
+                keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
+                encryptedKey = (Asn1OctetString) seq[3];
+            }
+            else
+            {
+                keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
+                encryptedKey = (Asn1OctetString) seq[2];
+            }
+        }
+
+		/**
+         * return a PasswordRecipientInfo 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 object held by the
+         *          tagged object cannot be converted.
+         */
+        public static PasswordRecipientInfo GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
+        }
+
+		/**
+         * return a PasswordRecipientInfo object from the given object.
+         *
+         * @param obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static PasswordRecipientInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is PasswordRecipientInfo)
+                return (PasswordRecipientInfo) obj;
+
+			if (obj is Asn1Sequence)
+                return new PasswordRecipientInfo((Asn1Sequence) obj);
+
+			throw new ArgumentException("Invalid PasswordRecipientInfo: " + obj.GetType().Name);
+        }
+
+		public DerInteger Version
+		{
+			get { return version; }
+		}
+
+		public AlgorithmIdentifier KeyDerivationAlgorithm
+		{
+			get { return keyDerivationAlgorithm; }
+		}
+
+		public AlgorithmIdentifier KeyEncryptionAlgorithm
+		{
+			get { return keyEncryptionAlgorithm; }
+		}
+
+		public Asn1OctetString EncryptedKey
+		{
+			get { return encryptedKey; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * PasswordRecipientInfo ::= Sequence {
+         *   version CMSVersion,   -- Always set to 0
+         *   keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
+         *                             OPTIONAL,
+         *  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+         *  encryptedKey EncryptedKey }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(version);
+
+			if (keyDerivationAlgorithm != null)
+            {
+                v.Add(new DerTaggedObject(false, 0, keyDerivationAlgorithm));
+            }
+
+			v.Add(keyEncryptionAlgorithm, encryptedKey);
+
+			return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/RecipientEncryptedKey.cs b/crypto/src/asn1/cms/RecipientEncryptedKey.cs
new file mode 100644
index 000000000..5ba25a742
--- /dev/null
+++ b/crypto/src/asn1/cms/RecipientEncryptedKey.cs
@@ -0,0 +1,88 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class RecipientEncryptedKey
+		: Asn1Encodable
+	{
+		private readonly KeyAgreeRecipientIdentifier identifier;
+		private readonly Asn1OctetString encryptedKey;
+
+		private RecipientEncryptedKey(
+			Asn1Sequence seq)
+		{
+			identifier = KeyAgreeRecipientIdentifier.GetInstance(seq[0]);
+			encryptedKey = (Asn1OctetString) seq[1];
+		}
+
+		/**
+		 * return an RecipientEncryptedKey object 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 object held by the
+		 *          tagged object cannot be converted.
+		 */
+		public static RecipientEncryptedKey GetInstance(
+			Asn1TaggedObject	obj,
+			bool				isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+
+		/**
+		 * return a RecipientEncryptedKey object from the given object.
+		 *
+		 * @param obj the object we want converted.
+		 * @exception ArgumentException if the object cannot be converted.
+		 */
+		public static RecipientEncryptedKey GetInstance(
+			object obj)
+		{
+			if (obj == null || obj is RecipientEncryptedKey)
+			{
+				return (RecipientEncryptedKey) obj;
+			}
+
+			if (obj is Asn1Sequence)
+			{
+				return new RecipientEncryptedKey((Asn1Sequence) obj);
+			}
+
+			throw new ArgumentException("Invalid RecipientEncryptedKey: " + obj.GetType().FullName, "obj");
+		}
+
+		public RecipientEncryptedKey(
+			KeyAgreeRecipientIdentifier	id,
+			Asn1OctetString				encryptedKey)
+		{
+			this.identifier = id;
+			this.encryptedKey = encryptedKey;
+		}
+
+		public KeyAgreeRecipientIdentifier Identifier
+		{
+			get { return identifier; }
+		}
+
+		public Asn1OctetString EncryptedKey
+		{
+			get { return encryptedKey; }
+		}
+
+		/** 
+		 * Produce an object suitable for an Asn1OutputStream.
+		 * <pre>
+		 * RecipientEncryptedKey ::= SEQUENCE {
+		 *     rid KeyAgreeRecipientIdentifier,
+		 *     encryptedKey EncryptedKey
+		 * }
+		 * </pre>
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return new DerSequence(identifier, encryptedKey);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/RecipientIdentifier.cs b/crypto/src/asn1/cms/RecipientIdentifier.cs
new file mode 100644
index 000000000..4982bc16a
--- /dev/null
+++ b/crypto/src/asn1/cms/RecipientIdentifier.cs
@@ -0,0 +1,89 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class RecipientIdentifier
+        : Asn1Encodable, IAsn1Choice
+    {
+        private Asn1Encodable id;
+
+		public RecipientIdentifier(
+            IssuerAndSerialNumber id)
+        {
+            this.id = id;
+        }
+
+		public RecipientIdentifier(
+            Asn1OctetString id)
+        {
+            this.id = new DerTaggedObject(false, 0, id);
+        }
+
+		public RecipientIdentifier(
+            Asn1Object id)
+        {
+            this.id = id;
+        }
+
+		/**
+         * return a RecipientIdentifier object from the given object.
+         *
+         * @param o the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static RecipientIdentifier GetInstance(
+            object o)
+        {
+            if (o == null || o is RecipientIdentifier)
+                return (RecipientIdentifier)o;
+
+			if (o is IssuerAndSerialNumber)
+                return new RecipientIdentifier((IssuerAndSerialNumber) o);
+
+			if (o is Asn1OctetString)
+                return new RecipientIdentifier((Asn1OctetString) o);
+
+			if (o is Asn1Object)
+                return new RecipientIdentifier((Asn1Object) o);
+
+			throw new ArgumentException(
+              "Illegal object in RecipientIdentifier: " + o.GetType().Name);
+        }
+
+		public bool IsTagged
+		{
+			get { return (id is Asn1TaggedObject); }
+		}
+
+		public Asn1Encodable ID
+        {
+            get
+            {
+                if (id is Asn1TaggedObject)
+                {
+                    return Asn1OctetString.GetInstance((Asn1TaggedObject) id, false);
+                }
+
+				return IssuerAndSerialNumber.GetInstance(id);
+            }
+        }
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * RecipientIdentifier ::= CHOICE {
+         *     issuerAndSerialNumber IssuerAndSerialNumber,
+         *     subjectKeyIdentifier [0] SubjectKeyIdentifier
+         * }
+         *
+         * SubjectKeyIdentifier ::= OCTET STRING
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return id.ToAsn1Object();
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/RecipientInfo.cs b/crypto/src/asn1/cms/RecipientInfo.cs
new file mode 100644
index 000000000..daaf5a5e4
--- /dev/null
+++ b/crypto/src/asn1/cms/RecipientInfo.cs
@@ -0,0 +1,145 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class RecipientInfo
+        : Asn1Encodable, IAsn1Choice
+    {
+        internal Asn1Encodable info;
+
+		public RecipientInfo(
+            KeyTransRecipientInfo info)
+        {
+            this.info = info;
+        }
+
+		public RecipientInfo(
+            KeyAgreeRecipientInfo info)
+        {
+            this.info = new DerTaggedObject(false, 1, info);
+        }
+
+		public RecipientInfo(
+            KekRecipientInfo info)
+        {
+            this.info = new DerTaggedObject(false, 2, info);
+        }
+
+		public RecipientInfo(
+            PasswordRecipientInfo info)
+        {
+            this.info = new DerTaggedObject(false, 3, info);
+        }
+
+		public RecipientInfo(
+            OtherRecipientInfo info)
+        {
+            this.info = new DerTaggedObject(false, 4, info);
+        }
+
+		public RecipientInfo(
+            Asn1Object   info)
+        {
+            this.info = info;
+        }
+
+		public static RecipientInfo GetInstance(
+            object o)
+        {
+            if (o == null || o is RecipientInfo)
+                return (RecipientInfo) o;
+
+			if (o is Asn1Sequence)
+                return new RecipientInfo((Asn1Sequence) o);
+
+			if (o is Asn1TaggedObject)
+                return new RecipientInfo((Asn1TaggedObject) o);
+
+			throw new ArgumentException("unknown object in factory: " + o.GetType().Name);
+        }
+
+		public DerInteger Version
+        {
+			get
+			{
+				if (info is Asn1TaggedObject)
+				{
+					Asn1TaggedObject o = (Asn1TaggedObject) info;
+
+					switch (o.TagNo)
+					{
+						case 1:
+							return KeyAgreeRecipientInfo.GetInstance(o, false).Version;
+						case 2:
+							return GetKekInfo(o).Version;
+						case 3:
+							return PasswordRecipientInfo.GetInstance(o, false).Version;
+						case 4:
+							return new DerInteger(0);    // no syntax version for OtherRecipientInfo
+						default:
+							throw new InvalidOperationException("unknown tag");
+					}
+				}
+
+				return KeyTransRecipientInfo.GetInstance(info).Version;
+			}
+        }
+
+		public bool IsTagged
+		{
+			get { return info is Asn1TaggedObject; }
+		}
+
+		public Asn1Encodable Info
+        {
+			get
+			{
+				if (info is Asn1TaggedObject)
+				{
+					Asn1TaggedObject o = (Asn1TaggedObject) info;
+
+					switch (o.TagNo)
+					{
+						case 1:
+							return KeyAgreeRecipientInfo.GetInstance(o, false);
+						case 2:
+							return GetKekInfo(o);
+						case 3:
+							return PasswordRecipientInfo.GetInstance(o, false);
+						case 4:
+							return OtherRecipientInfo.GetInstance(o, false);
+						default:
+							throw new InvalidOperationException("unknown tag");
+					}
+				}
+
+				return KeyTransRecipientInfo.GetInstance(info);
+			}
+        }
+
+		private KekRecipientInfo GetKekInfo(
+			Asn1TaggedObject o)
+		{
+			// For compatibility with erroneous version, we don't always pass 'false' here
+			return KekRecipientInfo.GetInstance(o, o.IsExplicit());
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * RecipientInfo ::= CHOICE {
+         *     ktri KeyTransRecipientInfo,
+         *     kari [1] KeyAgreeRecipientInfo,
+         *     kekri [2] KekRecipientInfo,
+         *     pwri [3] PasswordRecipientInfo,
+         *     ori [4] OtherRecipientInfo }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return info.ToAsn1Object();
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
new file mode 100644
index 000000000..f3e45644b
--- /dev/null
+++ b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
@@ -0,0 +1,137 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class RecipientKeyIdentifier
+        : Asn1Encodable
+    {
+        private Asn1OctetString      subjectKeyIdentifier;
+        private DerGeneralizedTime   date;
+        private OtherKeyAttribute    other;
+
+		public RecipientKeyIdentifier(
+            Asn1OctetString         subjectKeyIdentifier,
+            DerGeneralizedTime      date,
+            OtherKeyAttribute       other)
+        {
+            this.subjectKeyIdentifier = subjectKeyIdentifier;
+            this.date = date;
+            this.other = other;
+        }
+		
+		public RecipientKeyIdentifier(
+			byte[] subjectKeyIdentifier)
+			: this(subjectKeyIdentifier, null, null)
+		{
+		}
+
+		public RecipientKeyIdentifier(
+			byte[]				subjectKeyIdentifier,
+			DerGeneralizedTime	date,
+			OtherKeyAttribute	other)
+		{
+			this.subjectKeyIdentifier = new DerOctetString(subjectKeyIdentifier);
+			this.date = date;
+			this.other = other;
+		}
+
+		public RecipientKeyIdentifier(
+            Asn1Sequence seq)
+        {
+            subjectKeyIdentifier = Asn1OctetString.GetInstance(
+				seq[0]);
+
+			switch(seq.Count)
+            {
+				case 1:
+					break;
+				case 2:
+					if (seq[1] is DerGeneralizedTime)
+					{
+						date = (DerGeneralizedTime) seq[1];
+					}
+					else
+					{
+						other = OtherKeyAttribute.GetInstance(seq[2]);
+					}
+					break;
+				case 3:
+					date  = (DerGeneralizedTime) seq[1];
+					other = OtherKeyAttribute.GetInstance(seq[2]);
+					break;
+				default:
+					throw new ArgumentException("Invalid RecipientKeyIdentifier");
+            }
+        }
+
+		/**
+         * return a RecipientKeyIdentifier object from a tagged object.
+         *
+         * @param _ato the tagged object holding the object we want.
+         * @param _explicit true if the object is meant to be explicitly
+         *              tagged false otherwise.
+         * @exception ArgumentException if the object held by the
+         *          tagged object cannot be converted.
+         */
+        public static RecipientKeyIdentifier GetInstance(
+			Asn1TaggedObject	ato,
+			bool				explicitly)
+		{
+            return GetInstance(Asn1Sequence.GetInstance(ato, explicitly));
+        }
+
+		/**
+         * return a RecipientKeyIdentifier object from the given object.
+         *
+         * @param _obj the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static RecipientKeyIdentifier GetInstance(
+			object obj)
+		{
+            if (obj == null || obj is RecipientKeyIdentifier)
+                return (RecipientKeyIdentifier) obj;
+
+			if (obj is Asn1Sequence)
+				return new RecipientKeyIdentifier((Asn1Sequence) obj);
+
+			throw new ArgumentException("Invalid RecipientKeyIdentifier: " + obj.GetType().Name);
+        }
+
+		public Asn1OctetString SubjectKeyIdentifier
+		{
+			get { return subjectKeyIdentifier; }
+		}
+
+		public DerGeneralizedTime Date
+		{
+			get { return date; }
+		}
+
+		public OtherKeyAttribute OtherKeyAttribute
+		{
+			get { return other; }
+		}
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * RecipientKeyIdentifier ::= Sequence {
+         *     subjectKeyIdentifier SubjectKeyIdentifier,
+         *     date GeneralizedTime OPTIONAL,
+         *     other OtherKeyAttribute OPTIONAL
+         * }
+         *
+         * SubjectKeyIdentifier ::= OCTET STRING
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(subjectKeyIdentifier);
+			v.AddOptional(date, other);
+			return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/SCVPReqRes.cs b/crypto/src/asn1/cms/SCVPReqRes.cs
new file mode 100644
index 000000000..486979a29
--- /dev/null
+++ b/crypto/src/asn1/cms/SCVPReqRes.cs
@@ -0,0 +1,77 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class ScvpReqRes
+        : Asn1Encodable
+    {
+        private readonly ContentInfo request;
+        private readonly ContentInfo response;
+
+        public static ScvpReqRes GetInstance(object  obj)
+        {
+            if (obj is ScvpReqRes)
+                return (ScvpReqRes)obj;
+            if (obj != null)
+                return new ScvpReqRes(Asn1Sequence.GetInstance(obj));
+            return null;
+        }
+
+        private ScvpReqRes(Asn1Sequence seq)
+        {
+            if (seq[0] is Asn1TaggedObject)
+            {
+                this.request = ContentInfo.GetInstance(Asn1TaggedObject.GetInstance(seq[0]), true);
+                this.response = ContentInfo.GetInstance(seq[1]);
+            }
+            else
+            {
+                this.request = null;
+                this.response = ContentInfo.GetInstance(seq[0]);
+            }
+        }
+
+        public ScvpReqRes(ContentInfo response)
+            : this(null, response)
+        {
+        }
+
+        public ScvpReqRes(ContentInfo request, ContentInfo response)
+        {
+            this.request = request;
+            this.response = response;
+        }
+
+        public virtual ContentInfo Request
+        {
+            get { return request; }
+        }
+
+        public virtual ContentInfo Response
+        {
+            get { return response; }
+        }
+
+        /**
+         * <pre>
+         *    ScvpReqRes ::= SEQUENCE {
+         *    request  [0] EXPLICIT ContentInfo OPTIONAL,
+         *    response     ContentInfo }
+         * </pre>
+         * @return  the ASN.1 primitive representation.
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector();
+
+            if (request != null)
+            {
+                v.Add(new DerTaggedObject(true, 0, request));
+            }
+
+            v.Add(response);
+
+            return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/SignedData.cs b/crypto/src/asn1/cms/SignedData.cs
new file mode 100644
index 000000000..6cea79a49
--- /dev/null
+++ b/crypto/src/asn1/cms/SignedData.cs
@@ -0,0 +1,287 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    /**
+     * a signed data object.
+     */
+    public class SignedData
+        : Asn1Encodable
+    {
+        private static readonly DerInteger Version1 = new DerInteger(1);
+        private static readonly DerInteger Version3 = new DerInteger(3);
+        private static readonly DerInteger Version4 = new DerInteger(4);
+        private static readonly DerInteger Version5 = new DerInteger(5);
+
+        private readonly DerInteger		version;
+        private readonly Asn1Set		digestAlgorithms;
+        private readonly ContentInfo	contentInfo;
+        private readonly Asn1Set		certificates;
+        private readonly Asn1Set		crls;
+        private readonly Asn1Set		signerInfos;
+        private readonly bool			certsBer;
+        private readonly bool		    crlsBer;
+
+        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: " + obj.GetType().FullName, "obj");
+        }
+
+        public SignedData(
+            Asn1Set     digestAlgorithms,
+            ContentInfo contentInfo,
+            Asn1Set     certificates,
+            Asn1Set     crls,
+            Asn1Set     signerInfos)
+        {
+            this.version = CalculateVersion(contentInfo.ContentType, certificates, crls, signerInfos);
+            this.digestAlgorithms = digestAlgorithms;
+            this.contentInfo = contentInfo;
+            this.certificates = certificates;
+            this.crls = crls;
+            this.signerInfos = signerInfos;
+            this.crlsBer = crls is BerSet;
+            this.certsBer = certificates is BerSet;
+        }
+
+        // RFC3852, section 5.1:
+        // IF ((certificates is present) AND
+        //    (any certificates with a type of other are present)) OR
+        //    ((crls is present) AND
+        //    (any crls with a type of other are present))
+        // THEN version MUST be 5
+        // ELSE
+        //    IF (certificates is present) AND
+        //       (any version 2 attribute certificates are present)
+        //    THEN version MUST be 4
+        //    ELSE
+        //       IF ((certificates is present) AND
+        //          (any version 1 attribute certificates are present)) OR
+        //          (any SignerInfo structures are version 3) OR
+        //          (encapContentInfo eContentType is other than id-data)
+        //       THEN version MUST be 3
+        //       ELSE version MUST be 1
+        //
+        private DerInteger CalculateVersion(
+            DerObjectIdentifier	contentOid,
+            Asn1Set				certs,
+            Asn1Set				crls,
+            Asn1Set				signerInfs)
+        {
+            bool otherCert = false;
+            bool otherCrl = false;
+            bool attrCertV1Found = false;
+            bool attrCertV2Found = false;
+
+            if (certs != null)
+            {
+                foreach (object obj in certs)
+                {
+                    if (obj is Asn1TaggedObject)
+                    {
+                        Asn1TaggedObject tagged = (Asn1TaggedObject)obj;
+
+                        if (tagged.TagNo == 1)
+                        {
+                            attrCertV1Found = true;
+                        }
+                        else if (tagged.TagNo == 2)
+                        {
+                            attrCertV2Found = true;
+                        }
+                        else if (tagged.TagNo == 3)
+                        {
+                            otherCert = true;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (otherCert)
+            {
+                return Version5;
+            }
+
+            if (crls != null)
+            {
+                foreach (object obj in crls)
+                {
+                    if (obj is Asn1TaggedObject)
+                    {
+                        otherCrl = true;
+                        break;
+                    }
+                }
+            }
+
+            if (otherCrl)
+            {
+                return Version5;
+            }
+
+            if (attrCertV2Found)
+            {
+                return Version4;
+            }
+
+            if (attrCertV1Found || !CmsObjectIdentifiers.Data.Equals(contentOid) || CheckForVersion3(signerInfs))
+            {
+                return Version3;
+            }
+
+            return Version1;
+        }
+
+        private bool CheckForVersion3(
+            Asn1Set signerInfs)
+        {
+            foreach (object obj in signerInfs)
+            {
+                SignerInfo s = SignerInfo.GetInstance(obj);
+
+                if (s.Version.Value.IntValue == 3)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        private SignedData(
+            Asn1Sequence seq)
+        {
+            IEnumerator e = seq.GetEnumerator();
+
+            e.MoveNext();
+            version = (DerInteger)e.Current;
+
+            e.MoveNext();
+            digestAlgorithms = ((Asn1Set)e.Current);
+
+            e.MoveNext();
+            contentInfo = ContentInfo.GetInstance(e.Current);
+
+            while (e.MoveNext())
+            {
+                Asn1Object o = (Asn1Object)e.Current;
+
+                //
+                // an interesting feature of SignedData is that there appear
+                // to be varying implementations...
+                // for the moment we ignore anything which doesn't fit.
+                //
+                if (o is Asn1TaggedObject)
+                {
+                    Asn1TaggedObject tagged = (Asn1TaggedObject)o;
+
+                    switch (tagged.TagNo)
+                    {
+                        case 0:
+                            certsBer = tagged is BerTaggedObject;
+                            certificates = Asn1Set.GetInstance(tagged, false);
+                            break;
+                        case 1:
+                            crlsBer = tagged is BerTaggedObject;
+                            crls = Asn1Set.GetInstance(tagged, false);
+                            break;
+                        default:
+                            throw new ArgumentException("unknown tag value " + tagged.TagNo);
+                    }
+                }
+                else
+                {
+                    signerInfos = (Asn1Set) o;
+                }
+            }
+        }
+
+        public DerInteger Version
+        {
+            get { return version; }
+        }
+
+        public Asn1Set DigestAlgorithms
+        {
+            get { return digestAlgorithms; }
+        }
+
+        public ContentInfo EncapContentInfo
+        {
+            get { return contentInfo; }
+        }
+
+        public Asn1Set Certificates
+        {
+            get { return certificates; }
+        }
+
+        public Asn1Set CRLs
+        {
+            get { return crls; }
+        }
+
+        public Asn1Set SignerInfos
+        {
+            get { return signerInfos; }
+        }
+
+        /**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * SignedData ::= Sequence {
+         *     version CMSVersion,
+         *     digestAlgorithms DigestAlgorithmIdentifiers,
+         *     encapContentInfo EncapsulatedContentInfo,
+         *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
+         *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+         *     signerInfos SignerInfos
+         *   }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(
+                version, digestAlgorithms, contentInfo);
+
+            if (certificates != null)
+            {
+                if (certsBer)
+                {
+                    v.Add(new BerTaggedObject(false, 0, certificates));
+                }
+                else
+                {
+                    v.Add(new DerTaggedObject(false, 0, certificates));
+                }
+            }
+
+            if (crls != null)
+            {
+                if (crlsBer)
+                {
+                    v.Add(new BerTaggedObject(false, 1, crls));
+                }
+                else
+                {
+                    v.Add(new DerTaggedObject(false, 1, crls));
+                }
+            }
+
+            v.Add(signerInfos);
+
+            return new BerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/SignedDataParser.cs b/crypto/src/asn1/cms/SignedDataParser.cs
new file mode 100644
index 000000000..341309263
--- /dev/null
+++ b/crypto/src/asn1/cms/SignedDataParser.cs
@@ -0,0 +1,112 @@
+using System;
+using System.IO;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	/**
+	* <pre>
+	* SignedData ::= SEQUENCE {
+	*     version CMSVersion,
+	*     digestAlgorithms DigestAlgorithmIdentifiers,
+	*     encapContentInfo EncapsulatedContentInfo,
+	*     certificates [0] IMPLICIT CertificateSet OPTIONAL,
+	*     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+	*     signerInfos SignerInfos
+	*   }
+	* </pre>
+	*/
+	public class SignedDataParser
+	{
+		private Asn1SequenceParser	_seq;
+		private DerInteger			_version;
+		private object				_nextObject;
+		private bool				_certsCalled;
+		private bool				_crlsCalled;
+
+		public static SignedDataParser GetInstance(
+			object o)
+		{
+			if (o is Asn1Sequence)
+				return new SignedDataParser(((Asn1Sequence)o).Parser);
+
+			if (o is Asn1SequenceParser)
+				return new SignedDataParser((Asn1SequenceParser)o);
+
+			throw new IOException("unknown object encountered: " + o.GetType().Name);
+		}
+
+		public SignedDataParser(
+			Asn1SequenceParser seq)
+		{
+			this._seq = seq;
+			this._version = (DerInteger)seq.ReadObject();
+		}
+
+		public DerInteger Version
+		{
+			get { return _version; }
+		}
+
+		public Asn1SetParser GetDigestAlgorithms()
+		{
+			return (Asn1SetParser)_seq.ReadObject();
+		}
+
+		public ContentInfoParser GetEncapContentInfo()
+		{
+			return new ContentInfoParser((Asn1SequenceParser)_seq.ReadObject());
+		}
+
+		public Asn1SetParser GetCertificates()
+		{
+			_certsCalled = true;
+			_nextObject = _seq.ReadObject();
+
+			if (_nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)_nextObject).TagNo == 0)
+			{
+				Asn1SetParser certs = (Asn1SetParser)((Asn1TaggedObjectParser)_nextObject).GetObjectParser(Asn1Tags.Set, false);
+				_nextObject = null;
+
+				return certs;
+			}
+
+			return null;
+		}
+
+		public Asn1SetParser GetCrls()
+		{
+			if (!_certsCalled)
+				throw new IOException("GetCerts() has not been called.");
+
+			_crlsCalled = true;
+
+			if (_nextObject == null)
+			{
+				_nextObject = _seq.ReadObject();
+			}
+
+			if (_nextObject is Asn1TaggedObjectParser && ((Asn1TaggedObjectParser)_nextObject).TagNo == 1)
+			{
+				Asn1SetParser crls = (Asn1SetParser)((Asn1TaggedObjectParser)_nextObject).GetObjectParser(Asn1Tags.Set, false);
+				_nextObject = null;
+
+				return crls;
+			}
+
+			return null;
+		}
+
+		public Asn1SetParser GetSignerInfos()
+		{
+			if (!_certsCalled || !_crlsCalled)
+				throw new IOException("GetCerts() and/or GetCrls() has not been called.");
+
+			if (_nextObject == null)
+			{
+				_nextObject = _seq.ReadObject();
+			}
+
+			return (Asn1SetParser)_nextObject;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/SignerIdentifier.cs b/crypto/src/asn1/cms/SignerIdentifier.cs
new file mode 100644
index 000000000..5742cee75
--- /dev/null
+++ b/crypto/src/asn1/cms/SignerIdentifier.cs
@@ -0,0 +1,89 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class SignerIdentifier
+        : Asn1Encodable, IAsn1Choice
+    {
+        private Asn1Encodable id;
+
+		public SignerIdentifier(
+            IssuerAndSerialNumber id)
+        {
+            this.id = id;
+        }
+
+		public SignerIdentifier(
+            Asn1OctetString id)
+        {
+            this.id = new DerTaggedObject(false, 0, id);
+        }
+
+		public SignerIdentifier(
+            Asn1Object id)
+        {
+            this.id = id;
+        }
+
+		/**
+         * return a SignerIdentifier object from the given object.
+         *
+         * @param o the object we want converted.
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static SignerIdentifier GetInstance(
+            object o)
+        {
+            if (o == null || o is SignerIdentifier)
+                return (SignerIdentifier) o;
+
+			if (o is IssuerAndSerialNumber)
+                return new SignerIdentifier((IssuerAndSerialNumber) o);
+
+			if (o is Asn1OctetString)
+                return new SignerIdentifier((Asn1OctetString) o);
+
+			if (o is Asn1Object)
+                return new SignerIdentifier((Asn1Object) o);
+
+			throw new ArgumentException(
+				"Illegal object in SignerIdentifier: " + o.GetType().Name);
+        }
+
+		public bool IsTagged
+		{
+			get { return (id is Asn1TaggedObject); }
+		}
+
+		public Asn1Encodable ID
+        {
+            get
+            {
+                if (id is Asn1TaggedObject)
+                {
+                    return Asn1OctetString.GetInstance((Asn1TaggedObject)id, false);
+                }
+
+				return id;
+            }
+        }
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * SignerIdentifier ::= CHOICE {
+         *     issuerAndSerialNumber IssuerAndSerialNumber,
+         *     subjectKeyIdentifier [0] SubjectKeyIdentifier
+         * }
+         *
+         * SubjectKeyIdentifier ::= OCTET STRING
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return id.ToAsn1Object();
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/SignerInfo.cs b/crypto/src/asn1/cms/SignerInfo.cs
new file mode 100644
index 000000000..a4e893d96
--- /dev/null
+++ b/crypto/src/asn1/cms/SignerInfo.cs
@@ -0,0 +1,185 @@
+using System;
+using System.Collections;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class SignerInfo
+        : Asn1Encodable
+    {
+        private DerInteger              version;
+        private SignerIdentifier        sid;
+        private AlgorithmIdentifier     digAlgorithm;
+        private Asn1Set                 authenticatedAttributes;
+        private AlgorithmIdentifier     digEncryptionAlgorithm;
+        private Asn1OctetString         encryptedDigest;
+        private Asn1Set                 unauthenticatedAttributes;
+
+        public static SignerInfo GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is SignerInfo)
+                return (SignerInfo) obj;
+
+            if (obj is Asn1Sequence)
+                return new SignerInfo((Asn1Sequence) obj);
+
+            throw new ArgumentException("Unknown object in factory: " + obj.GetType().FullName, "obj");
+        }
+
+        public SignerInfo(
+            SignerIdentifier        sid,
+            AlgorithmIdentifier     digAlgorithm,
+            Asn1Set                 authenticatedAttributes,
+            AlgorithmIdentifier     digEncryptionAlgorithm,
+            Asn1OctetString         encryptedDigest,
+            Asn1Set                 unauthenticatedAttributes)
+        {
+            this.version = new DerInteger(sid.IsTagged ? 3 : 1);
+            this.sid = sid;
+            this.digAlgorithm = digAlgorithm;
+            this.authenticatedAttributes = authenticatedAttributes;
+            this.digEncryptionAlgorithm = digEncryptionAlgorithm;
+            this.encryptedDigest = encryptedDigest;
+            this.unauthenticatedAttributes = unauthenticatedAttributes;
+        }
+
+        public SignerInfo(
+            SignerIdentifier        sid,
+            AlgorithmIdentifier     digAlgorithm,
+            Attributes              authenticatedAttributes,
+            AlgorithmIdentifier     digEncryptionAlgorithm,
+            Asn1OctetString         encryptedDigest,
+            Attributes              unauthenticatedAttributes)
+        {
+            this.version = new DerInteger(sid.IsTagged ? 3 : 1);
+            this.sid = sid;
+            this.digAlgorithm = digAlgorithm;
+            this.authenticatedAttributes = Asn1Set.GetInstance(authenticatedAttributes);
+            this.digEncryptionAlgorithm = digEncryptionAlgorithm;
+            this.encryptedDigest = encryptedDigest;
+            this.unauthenticatedAttributes = Asn1Set.GetInstance(unauthenticatedAttributes);
+        }
+
+        [Obsolete("Use 'GetInstance' instead")]
+        public SignerInfo(
+            Asn1Sequence seq)
+        {
+            IEnumerator e = seq.GetEnumerator();
+
+            e.MoveNext();
+            version = (DerInteger) e.Current;
+
+            e.MoveNext();
+            sid = SignerIdentifier.GetInstance(e.Current);
+
+            e.MoveNext();
+            digAlgorithm = AlgorithmIdentifier.GetInstance(e.Current);
+
+            e.MoveNext();
+            object obj = e.Current;
+
+            if (obj is Asn1TaggedObject)
+            {
+                authenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) obj, false);
+
+                e.MoveNext();
+                digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(e.Current);
+            }
+            else
+            {
+                authenticatedAttributes = null;
+                digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(obj);
+            }
+
+            e.MoveNext();
+            encryptedDigest = DerOctetString.GetInstance(e.Current);
+
+            if (e.MoveNext())
+            {
+                unauthenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject) e.Current, false);
+            }
+            else
+            {
+                unauthenticatedAttributes = null;
+            }
+        }
+
+        public DerInteger Version
+        {
+            get { return version; }
+        }
+
+        public SignerIdentifier SignerID
+        {
+            get { return sid; }
+        }
+
+        public Asn1Set AuthenticatedAttributes
+        {
+            get { return authenticatedAttributes; }
+        }
+
+        public AlgorithmIdentifier DigestAlgorithm
+        {
+            get { return digAlgorithm; }
+        }
+
+        public Asn1OctetString EncryptedDigest
+        {
+            get { return encryptedDigest; }
+        }
+
+        public AlgorithmIdentifier DigestEncryptionAlgorithm
+        {
+            get { return digEncryptionAlgorithm; }
+        }
+
+        public Asn1Set UnauthenticatedAttributes
+        {
+            get { return unauthenticatedAttributes; }
+        }
+
+        /**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         *  SignerInfo ::= Sequence {
+         *      version Version,
+         *      SignerIdentifier sid,
+         *      digestAlgorithm DigestAlgorithmIdentifier,
+         *      authenticatedAttributes [0] IMPLICIT Attributes OPTIONAL,
+         *      digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+         *      encryptedDigest EncryptedDigest,
+         *      unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
+         *  }
+         *
+         *  EncryptedDigest ::= OCTET STRING
+         *
+         *  DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+         *
+         *  DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(
+                version, sid, digAlgorithm);
+
+            if (authenticatedAttributes != null)
+            {
+                v.Add(new DerTaggedObject(false, 0, authenticatedAttributes));
+            }
+
+            v.Add(digEncryptionAlgorithm, encryptedDigest);
+
+            if (unauthenticatedAttributes != null)
+            {
+                v.Add(new DerTaggedObject(false, 1, unauthenticatedAttributes));
+            }
+
+            return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/Time.cs b/crypto/src/asn1/cms/Time.cs
new file mode 100644
index 000000000..d113bfa2e
--- /dev/null
+++ b/crypto/src/asn1/cms/Time.cs
@@ -0,0 +1,118 @@
+using System;
+using System.Globalization;
+
+using Org.BouncyCastle.Asn1;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+    public class Time
+        : Asn1Encodable, IAsn1Choice
+    {
+        private readonly Asn1Object time;
+
+		public static Time GetInstance(
+            Asn1TaggedObject	obj,
+            bool				explicitly)
+        {
+            return GetInstance(obj.GetObject());
+        }
+
+		public Time(
+            Asn1Object time)
+        {
+            if (!(time is DerUtcTime)
+                && !(time is DerGeneralizedTime))
+            {
+                throw new ArgumentException("unknown object passed to Time");
+            }
+
+			this.time = time;
+        }
+
+		/**
+         * creates a time object from a given date - if the date is between 1950
+         * and 2049 a UTCTime object is Generated, otherwise a GeneralizedTime
+         * is used.
+         */
+        public Time(
+            DateTime date)
+        {
+            string d = date.ToString("yyyyMMddHHmmss") + "Z";
+
+			int year = int.Parse(d.Substring(0, 4));
+
+			if (year < 1950 || year > 2049)
+            {
+                time = new DerGeneralizedTime(d);
+            }
+            else
+            {
+                time = new DerUtcTime(d.Substring(2));
+            }
+        }
+
+		public static Time GetInstance(
+            object obj)
+        {
+            if (obj == null || obj is Time)
+                return (Time)obj;
+
+			if (obj is DerUtcTime)
+                return new Time((DerUtcTime)obj);
+
+			if (obj is DerGeneralizedTime)
+                return new Time((DerGeneralizedTime)obj);
+
+			throw new ArgumentException("unknown object in factory: " + obj.GetType().Name, "obj");
+        }
+
+		public string TimeString
+        {
+			get
+			{
+				if (time is DerUtcTime)
+				{
+					return ((DerUtcTime)time).AdjustedTimeString;
+				}
+				else
+				{
+					return ((DerGeneralizedTime)time).GetTime();
+				}
+			}
+        }
+
+		public DateTime Date
+        {
+			get
+			{
+				try
+				{
+					if (time is DerUtcTime)
+					{
+						return ((DerUtcTime)time).ToAdjustedDateTime();
+					}
+
+					return ((DerGeneralizedTime)time).ToDateTime();
+				}
+				catch (FormatException e)
+				{
+					// this should never happen
+					throw new InvalidOperationException("invalid date string: " + e.Message);
+				}
+			}
+        }
+
+		/**
+         * Produce an object suitable for an Asn1OutputStream.
+         * <pre>
+         * Time ::= CHOICE {
+         *             utcTime        UTCTime,
+         *             generalTime    GeneralizedTime }
+         * </pre>
+         */
+        public override Asn1Object ToAsn1Object()
+        {
+            return time;
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/TimeStampAndCRL.cs b/crypto/src/asn1/cms/TimeStampAndCRL.cs
new file mode 100644
index 000000000..4cb5f2a52
--- /dev/null
+++ b/crypto/src/asn1/cms/TimeStampAndCRL.cs
@@ -0,0 +1,62 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class TimeStampAndCrl
+		: Asn1Encodable
+	{
+		private ContentInfo timeStamp;
+		private X509.CertificateList crl;
+
+		public TimeStampAndCrl(ContentInfo timeStamp)
+		{
+			this.timeStamp = timeStamp;
+		}
+
+		private TimeStampAndCrl(Asn1Sequence seq)
+		{
+			this.timeStamp = ContentInfo.GetInstance(seq[0]);
+			if (seq.Count == 2)
+			{
+				this.crl = X509.CertificateList.GetInstance(seq[1]);
+			}
+		}
+
+		public static TimeStampAndCrl GetInstance(object obj)
+		{
+			if (obj is TimeStampAndCrl)
+				return (TimeStampAndCrl)obj;
+
+			if (obj != null)
+				return new TimeStampAndCrl(Asn1Sequence.GetInstance(obj));
+
+			return null;
+		}
+
+		public virtual ContentInfo TimeStampToken
+		{
+			get { return this.timeStamp; }
+		}
+
+		public virtual X509.CertificateList Crl
+		{
+			get { return this.crl; }
+		}
+
+		/**
+		 * <pre>
+		 * TimeStampAndCRL ::= SEQUENCE {
+		 *     timeStamp   TimeStampToken,          -- according to RFC 3161
+		 *     crl         CertificateList OPTIONAL -- according to RFC 5280
+		 *  }
+		 * </pre>
+		 * @return
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(timeStamp);
+			v.AddOptional(crl);
+			return new DerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/TimeStampTokenEvidence.cs b/crypto/src/asn1/cms/TimeStampTokenEvidence.cs
new file mode 100644
index 000000000..8625d058e
--- /dev/null
+++ b/crypto/src/asn1/cms/TimeStampTokenEvidence.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class TimeStampTokenEvidence
+		: Asn1Encodable
+	{
+		private TimeStampAndCrl[] timeStampAndCrls;
+
+		public TimeStampTokenEvidence(TimeStampAndCrl[] timeStampAndCrls)
+		{
+			this.timeStampAndCrls = timeStampAndCrls;
+		}
+
+		public TimeStampTokenEvidence(TimeStampAndCrl timeStampAndCrl)
+		{
+			this.timeStampAndCrls = new TimeStampAndCrl[]{ timeStampAndCrl };
+		}
+
+		private TimeStampTokenEvidence(Asn1Sequence seq)
+		{
+			this.timeStampAndCrls = new TimeStampAndCrl[seq.Count];
+
+			int count = 0;
+
+			foreach (Asn1Encodable ae in seq)
+			{
+				this.timeStampAndCrls[count++] = TimeStampAndCrl.GetInstance(ae.ToAsn1Object());
+			}
+		}
+
+		public static TimeStampTokenEvidence GetInstance(Asn1TaggedObject tagged, bool isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(tagged, isExplicit));
+		}
+
+		public static TimeStampTokenEvidence GetInstance(object obj)
+		{
+			if (obj is TimeStampTokenEvidence)
+				return (TimeStampTokenEvidence)obj;
+
+			if (obj != null)
+				return new TimeStampTokenEvidence(Asn1Sequence.GetInstance(obj));
+
+			return null;
+		}
+
+		public virtual TimeStampAndCrl[] ToTimeStampAndCrlArray()
+		{
+			return (TimeStampAndCrl[])timeStampAndCrls.Clone();
+		}
+
+		/**
+		 * <pre>
+		 * TimeStampTokenEvidence ::=
+		 *    SEQUENCE SIZE(1..MAX) OF TimeStampAndCrl
+		 * </pre>
+		 * @return
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			return new DerSequence(timeStampAndCrls);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/TimeStampedData.cs b/crypto/src/asn1/cms/TimeStampedData.cs
new file mode 100644
index 000000000..15448a923
--- /dev/null
+++ b/crypto/src/asn1/cms/TimeStampedData.cs
@@ -0,0 +1,95 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class TimeStampedData
+		: Asn1Encodable
+	{
+		private DerInteger version;
+		private DerIA5String dataUri;
+		private MetaData metaData;
+		private Asn1OctetString content;
+		private Evidence temporalEvidence;
+
+		public TimeStampedData(DerIA5String dataUri, MetaData metaData, Asn1OctetString content,
+			Evidence temporalEvidence)
+		{
+			this.version = new DerInteger(1);
+			this.dataUri = dataUri;
+			this.metaData = metaData;
+			this.content = content;
+			this.temporalEvidence = temporalEvidence;
+		}
+
+		private TimeStampedData(Asn1Sequence seq)
+		{
+			this.version = DerInteger.GetInstance(seq[0]);
+			
+			int index = 1;
+			if (seq[index] is DerIA5String)
+			{
+				this.dataUri = DerIA5String.GetInstance(seq[index++]);
+			}
+			if (seq[index] is MetaData || seq[index] is Asn1Sequence)
+			{
+				this.metaData = MetaData.GetInstance(seq[index++]);
+			}
+			if (seq[index] is Asn1OctetString)
+			{
+				this.content = Asn1OctetString.GetInstance(seq[index++]);
+			}
+			this.temporalEvidence = Evidence.GetInstance(seq[index]);
+		}
+
+		public static TimeStampedData GetInstance(object obj)
+		{
+			if (obj is TimeStampedData)
+				return (TimeStampedData)obj;
+
+			if (obj != null)
+				return new TimeStampedData(Asn1Sequence.GetInstance(obj));
+
+			return null;
+		}
+
+		public virtual DerIA5String DataUri
+		{
+			get { return dataUri; }
+		}
+
+		public MetaData MetaData
+		{
+			get { return metaData; }
+		}
+
+		public Asn1OctetString Content
+		{
+			get { return content; }
+		}
+
+		public Evidence TemporalEvidence
+		{
+			get { return temporalEvidence; }
+		}
+
+		/**
+		 * <pre>
+		 * TimeStampedData ::= SEQUENCE {
+		 *   version              INTEGER { v1(1) },
+		 *   dataUri              IA5String OPTIONAL,
+		 *   metaData             MetaData OPTIONAL,
+		 *   content              OCTET STRING OPTIONAL,
+		 *   temporalEvidence     Evidence
+		 * }
+		 * </pre>
+		 * @return
+		 */
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(version);
+			v.AddOptional(dataUri, metaData, content);
+			v.Add(temporalEvidence);
+			return new BerSequence(v);
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/TimeStampedDataParser.cs b/crypto/src/asn1/cms/TimeStampedDataParser.cs
new file mode 100644
index 000000000..90307bff9
--- /dev/null
+++ b/crypto/src/asn1/cms/TimeStampedDataParser.cs
@@ -0,0 +1,76 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
+{
+	public class TimeStampedDataParser
+	{
+		private DerInteger version;
+		private DerIA5String dataUri;
+		private MetaData metaData;
+		private Asn1OctetStringParser content;
+		private Evidence temporalEvidence;
+		private Asn1SequenceParser parser;
+		
+		private TimeStampedDataParser(Asn1SequenceParser parser)
+		{
+			this.parser = parser;
+			this.version = DerInteger.GetInstance(parser.ReadObject());
+
+			Asn1Object obj = parser.ReadObject().ToAsn1Object();
+
+			if (obj is DerIA5String)
+			{
+				this.dataUri = DerIA5String.GetInstance(obj);
+				obj = parser.ReadObject().ToAsn1Object();
+			}
+
+            if (//obj is MetaData ||
+                obj is Asn1SequenceParser)
+			{
+				this.metaData = MetaData.GetInstance(obj.ToAsn1Object());
+				obj = parser.ReadObject().ToAsn1Object();
+			}
+
+			if (obj is Asn1OctetStringParser)
+			{
+				this.content = (Asn1OctetStringParser)obj;
+			}
+		}
+
+		public static TimeStampedDataParser GetInstance(object obj)
+		{
+			if (obj is Asn1Sequence)
+				return new TimeStampedDataParser(((Asn1Sequence)obj).Parser);
+
+			if (obj is Asn1SequenceParser)
+				return new TimeStampedDataParser((Asn1SequenceParser)obj);
+
+			return null;
+		}
+		
+		public virtual DerIA5String DataUri
+		{
+			get { return dataUri; }
+		}
+
+		public virtual MetaData MetaData
+		{
+			get { return metaData; }
+		}
+
+		public virtual Asn1OctetStringParser Content
+		{
+			get { return content; }
+		}
+
+		public virtual Evidence GetTemporalEvidence()
+		{
+			if (temporalEvidence == null)
+			{
+				temporalEvidence = Evidence.GetInstance(parser.ReadObject().ToAsn1Object());
+			}
+
+			return temporalEvidence;
+		}
+	}
+}
diff --git a/crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs b/crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs
new file mode 100644
index 000000000..53c5c706b
--- /dev/null
+++ b/crypto/src/asn1/cms/ecc/MQVuserKeyingMaterial.cs
@@ -0,0 +1,103 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms.Ecc
+{
+	public class MQVuserKeyingMaterial
+		: Asn1Encodable
+	{
+		private OriginatorPublicKey	ephemeralPublicKey;
+		private Asn1OctetString		addedukm;
+
+		public MQVuserKeyingMaterial(
+			OriginatorPublicKey	ephemeralPublicKey,
+			Asn1OctetString		addedukm)
+		{
+			// TODO Check ephemeralPublicKey not null
+
+			this.ephemeralPublicKey = ephemeralPublicKey;
+			this.addedukm = addedukm;
+		}
+
+		private MQVuserKeyingMaterial(
+			Asn1Sequence seq)
+		{
+			// TODO Check seq has either 1 or 2 elements
+
+			this.ephemeralPublicKey = OriginatorPublicKey.GetInstance(seq[0]);
+
+			if (seq.Count > 1)
+			{
+				this.addedukm = Asn1OctetString.GetInstance(
+					(Asn1TaggedObject)seq[1], true);
+			}
+		}
+
+		/**
+		 * return an AuthEnvelopedData object 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.
+		 * @throws ArgumentException if the object held by the
+		 *                                  tagged object cannot be converted.
+		 */
+		public static MQVuserKeyingMaterial GetInstance(
+			Asn1TaggedObject	obj,
+			bool				isExplicit)
+		{
+			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+		}
+
+		/**
+		 * return an AuthEnvelopedData object from the given object.
+		 *
+		 * @param obj the object we want converted.
+		 * @throws ArgumentException if the object cannot be converted.
+		 */
+		public static MQVuserKeyingMaterial GetInstance(
+			object	obj)
+		{
+			if (obj == null || obj is MQVuserKeyingMaterial)
+			{
+				return (MQVuserKeyingMaterial)obj;
+			}
+
+			if (obj is Asn1Sequence)
+			{
+				return new MQVuserKeyingMaterial((Asn1Sequence)obj);
+			}
+
+			throw new ArgumentException("Invalid MQVuserKeyingMaterial: " + obj.GetType().Name);
+		}
+		
+		public OriginatorPublicKey EphemeralPublicKey
+		{
+			get { return ephemeralPublicKey; }
+		}
+
+		public Asn1OctetString AddedUkm
+		{
+			get { return addedukm; }
+		}
+
+		/**
+		* Produce an object suitable for an Asn1OutputStream.
+		* <pre>
+		* MQVuserKeyingMaterial ::= SEQUENCE {
+		*   ephemeralPublicKey OriginatorPublicKey,
+		*   addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL  }
+		* </pre>
+		*/
+		public override Asn1Object ToAsn1Object()
+		{
+			Asn1EncodableVector v = new Asn1EncodableVector(ephemeralPublicKey);
+
+			if (addedukm != null)
+			{
+				v.Add(new DerTaggedObject(true, 0, addedukm));
+			}
+
+			return new DerSequence(v);
+		}
+	}
+}