summary refs log tree commit diff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--crypto/src/asn1/Asn1Utilities.cs14
-rw-r--r--crypto/src/asn1/cms/Attribute.cs38
-rw-r--r--crypto/src/asn1/cms/AttributeTable.cs112
-rw-r--r--crypto/src/asn1/cms/Attributes.cs17
-rw-r--r--crypto/src/asn1/cms/AuthEnvelopedData.cs194
-rw-r--r--crypto/src/asn1/cms/AuthenticatedData.cs283
-rw-r--r--crypto/src/asn1/cms/CcmParameters.cs29
-rw-r--r--crypto/src/asn1/cms/CmsAlgorithmProtection.cs61
-rw-r--r--crypto/src/asn1/cms/CompressedData.cs50
-rw-r--r--crypto/src/asn1/cms/ContentInfo.cs54
-rw-r--r--crypto/src/asn1/cms/EncryptedContentInfo.cs60
-rw-r--r--crypto/src/asn1/cms/EncryptedData.cs62
-rw-r--r--crypto/src/asn1/cms/EnvelopedData.cs201
-rw-r--r--crypto/src/asn1/cms/Evidence.cs4
-rw-r--r--crypto/src/asn1/cms/GcmParameters.cs29
-rw-r--r--crypto/src/asn1/cms/IssuerAndSerialNumber.cs6
-rw-r--r--crypto/src/asn1/cms/KEKIdentifier.cs68
-rw-r--r--crypto/src/asn1/cms/KEKRecipientInfo.cs63
-rw-r--r--crypto/src/asn1/cms/KemRecipientInfo.cs28
-rw-r--r--crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs40
-rw-r--r--crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs87
-rw-r--r--crypto/src/asn1/cms/KeyTransRecipientInfo.cs72
-rw-r--r--crypto/src/asn1/cms/MetaData.cs116
-rw-r--r--crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs2
-rw-r--r--crypto/src/asn1/cms/OriginatorInfo.cs65
-rw-r--r--crypto/src/asn1/cms/OriginatorPublicKey.cs10
-rw-r--r--crypto/src/asn1/cms/OtherKeyAttribute.cs54
-rw-r--r--crypto/src/asn1/cms/OtherRecipientInfo.cs37
-rw-r--r--crypto/src/asn1/cms/OtherRevocationInfoFormat.cs39
-rw-r--r--crypto/src/asn1/cms/PasswordRecipientInfo.cs79
-rw-r--r--crypto/src/asn1/cms/RecipientEncryptedKey.cs44
-rw-r--r--crypto/src/asn1/cms/RecipientIdentifier.cs4
-rw-r--r--crypto/src/asn1/cms/RecipientInfo.cs138
-rw-r--r--crypto/src/asn1/cms/RecipientKeyIdentifier.cs85
-rw-r--r--crypto/src/asn1/cms/SCVPReqRes.cs49
-rw-r--r--crypto/src/asn1/cms/SignedData.cs354
-rw-r--r--crypto/src/asn1/cms/SignerIdentifier.cs31
-rw-r--r--crypto/src/asn1/cms/SignerInfo.cs157
-rw-r--r--crypto/src/asn1/cms/TimeStampAndCRL.cs48
-rw-r--r--crypto/src/asn1/cms/TimeStampTokenEvidence.cs32
-rw-r--r--crypto/src/asn1/cms/TimeStampedData.cs89
-rw-r--r--crypto/src/asn1/x509/CertificateList.cs31
42 files changed, 1333 insertions, 1703 deletions
diff --git a/crypto/src/asn1/Asn1Utilities.cs b/crypto/src/asn1/Asn1Utilities.cs
index cdef45646..169f1fb37 100644
--- a/crypto/src/asn1/Asn1Utilities.cs
+++ b/crypto/src/asn1/Asn1Utilities.cs
@@ -652,6 +652,20 @@ namespace Org.BouncyCastle.Asn1
 
         #region Sequence cursor
 
+        public static TResult ReadContextTagged<TState, TResult>(Asn1Sequence sequence, ref int sequencePosition,
+            int tagNo, TState state, Func<Asn1TaggedObject, TState, TResult> constructor)
+        {
+            return ReadTagged(sequence, ref sequencePosition, Asn1Tags.ContextSpecific, tagNo, state, constructor);
+        }
+
+        public static TResult ReadTagged<TState, TResult>(Asn1Sequence sequence, ref int sequencePosition, int tagClass,
+            int tagNo, TState state, Func<Asn1TaggedObject, TState, TResult> constructor)
+        {
+            var tagged = Asn1TaggedObject.GetInstance(sequence[sequencePosition++], tagClass, tagNo);
+
+            return constructor(tagged, state);
+        }
+
         public static TResult ReadOptional<TResult>(Asn1Sequence sequence, ref int sequencePosition,
             Func<Asn1Encodable, TResult> constructor)
             where TResult : class
diff --git a/crypto/src/asn1/cms/Attribute.cs b/crypto/src/asn1/cms/Attribute.cs
index 9e2ced198..f44646bbc 100644
--- a/crypto/src/asn1/cms/Attribute.cs
+++ b/crypto/src/asn1/cms/Attribute.cs
@@ -23,34 +23,29 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private DerObjectIdentifier	attrType;
-        private Asn1Set				attrValues;
+        private readonly DerObjectIdentifier m_attrType;
+        private readonly Asn1Set m_attrValues;
 
         [Obsolete("Use 'GetInstance' instead")]
-        public Attribute(
-            Asn1Sequence seq)
+        public Attribute(Asn1Sequence seq)
         {
-            attrType = (DerObjectIdentifier)seq[0];
-            attrValues = (Asn1Set)seq[1];
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_attrType = DerObjectIdentifier.GetInstance(seq[0]);
+            m_attrValues = Asn1Set.GetInstance(seq[1]);
         }
 
-		public Attribute(
-            DerObjectIdentifier attrType,
-            Asn1Set             attrValues)
+        public Attribute(DerObjectIdentifier attrType, Asn1Set attrValues)
         {
-            this.attrType = attrType;
-            this.attrValues = attrValues;
+            m_attrType = attrType ?? throw new ArgumentNullException(nameof(attrType));
+            m_attrValues = attrValues ?? throw new ArgumentNullException(nameof(attrValues));
         }
 
-        public DerObjectIdentifier AttrType
-		{
-			get { return attrType; }
-		}
+        public DerObjectIdentifier AttrType => m_attrType;
 
-		public Asn1Set AttrValues
-		{
-			get { return attrValues; }
-		}
+        public Asn1Set AttrValues => m_attrValues;
 
 		/**
         * Produce an object suitable for an Asn1OutputStream.
@@ -61,9 +56,6 @@ namespace Org.BouncyCastle.Asn1.Cms
         * }
         * </pre>
         */
-        public override Asn1Object ToAsn1Object()
-        {
-			return new DerSequence(attrType, attrValues);
-        }
+        public override Asn1Object ToAsn1Object() => new DerSequence(m_attrType, m_attrValues);
     }
 }
diff --git a/crypto/src/asn1/cms/AttributeTable.cs b/crypto/src/asn1/cms/AttributeTable.cs
index fc6d3cabc..188b651c1 100644
--- a/crypto/src/asn1/cms/AttributeTable.cs
+++ b/crypto/src/asn1/cms/AttributeTable.cs
@@ -14,22 +14,12 @@ namespace Org.BouncyCastle.Asn1.Cms
 
         public AttributeTable(Asn1EncodableVector v)
         {
-            m_attributes = new Dictionary<DerObjectIdentifier, object>(v.Count);
-
-            foreach (Asn1Encodable e in v)
-            {
-				AddAttribute(Attribute.GetInstance(e));
-            }
+            m_attributes = BuildAttributes(v);
         }
 
         public AttributeTable(Asn1Set s)
         {
-            m_attributes = new Dictionary<DerObjectIdentifier, object>(s.Count);
-
-            foreach (Asn1Encodable e in s)
-            {
-                AddAttribute(Attribute.GetInstance(e));
-            }
+            m_attributes = BuildAttributes(s);
         }
 
 		public AttributeTable(Attributes attrs)
@@ -37,34 +27,6 @@ namespace Org.BouncyCastle.Asn1.Cms
 		{
 		}
 
-		private void AddAttribute(Attribute a)
-        {
-			DerObjectIdentifier oid = a.AttrType;
-
-            if (!m_attributes.TryGetValue(oid, out object existingValue))
-            {
-                m_attributes[oid] = a;
-                return;
-            }
-
-            if (existingValue is IList<Attribute> existingList)
-            {
-                existingList.Add(a);
-                return;
-            }
-
-            if (existingValue is Attribute existingAttr)
-            {
-                var newList = new List<Attribute>();
-                newList.Add(existingAttr);
-                newList.Add(a);
-                m_attributes[oid] = newList;
-                return;
-            }
-
-            throw new InvalidOperationException();
-        }
-
 		/// <summary>Return the first attribute matching the given OBJECT IDENTIFIER</summary>
 		public Attribute this[DerObjectIdentifier oid]
 		{
@@ -73,7 +35,7 @@ namespace Org.BouncyCastle.Asn1.Cms
                 if (!m_attributes.TryGetValue(oid, out object existingValue))
                     return null;
 
-                if (existingValue is IList<Attribute> existingList)
+                if (existingValue is List<Attribute> existingList)
                     return existingList[0];
 
                 if (existingValue is Attribute existingAttr)
@@ -96,7 +58,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
             if (m_attributes.TryGetValue(oid, out object existingValue))
             {
-                if (existingValue is IList<Attribute> existingList)
+                if (existingValue is List<Attribute> existingList)
                 {
                     foreach (var attr in existingList)
                     {
@@ -124,7 +86,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
                 foreach (object existingValue in m_attributes.Values)
                 {
-                    if (existingValue is IList<Attribute> existingList)
+                    if (existingValue is List<Attribute> existingList)
                     {
                         total += existingList.Count;
                     }
@@ -153,7 +115,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
             foreach (object existingValue in m_attributes.Values)
             {
-                if (existingValue is IList<Attribute> existingList)
+                if (existingValue is List<Attribute> existingList)
                 {
                     foreach (Attribute existingAttr in existingList)
                     {
@@ -183,13 +145,12 @@ namespace Org.BouncyCastle.Asn1.Cms
             if (attributes == null || attributes.Length < 1)
                 return this;
 
-            var newTable = new AttributeTable(m_attributes);
+            var result = new AttributeTable(m_attributes);
             foreach (Attribute attribute in attributes)
             {
-                newTable.AddAttribute(attribute);
+                AddAttribute(result.m_attributes, attribute);
             }
-
-            return newTable;
+            return result;
         }
 
         /**
@@ -201,20 +162,57 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 */
         public AttributeTable Add(DerObjectIdentifier attrType, Asn1Encodable attrValue)
 		{
-			AttributeTable newTable = new AttributeTable(m_attributes);
-
-			newTable.AddAttribute(new Attribute(attrType, new DerSet(attrValue)));
-
-			return newTable;
+			AttributeTable result = new AttributeTable(m_attributes);
+            AddAttribute(result.m_attributes, new Attribute(attrType, new DerSet(attrValue)));
+			return result;
 		}
 
 		public AttributeTable Remove(DerObjectIdentifier attrType)
 		{
-			AttributeTable newTable = new AttributeTable(m_attributes);
-
-			newTable.m_attributes.Remove(attrType);
+            if (!m_attributes.ContainsKey(attrType))
+                return this;
 
-			return newTable;
+            AttributeTable result = new AttributeTable(m_attributes);
+			result.m_attributes.Remove(attrType);
+			return result;
 		}
+
+        private static void AddAttribute(Dictionary<DerObjectIdentifier, object> attributes, Attribute a)
+        {
+            DerObjectIdentifier oid = a.AttrType;
+
+            if (!attributes.TryGetValue(oid, out object existingValue))
+            {
+                attributes[oid] = a;
+                return;
+            }
+
+            if (existingValue is List<Attribute> existingList)
+            {
+                existingList.Add(a);
+                return;
+            }
+
+            if (existingValue is Attribute existingAttr)
+            {
+                var newList = new List<Attribute>();
+                newList.Add(existingAttr);
+                newList.Add(a);
+                attributes[oid] = newList;
+                return;
+            }
+
+            throw new InvalidOperationException();
+        }
+
+        private static Dictionary<DerObjectIdentifier, object> BuildAttributes(IEnumerable<Asn1Encodable> e)
+        {
+            var result = new Dictionary<DerObjectIdentifier, object>();
+            foreach (Asn1Encodable element in e)
+            {
+                AddAttribute(result, Attribute.GetInstance(element));
+            }
+            return result;
+        }
     }
 }
diff --git a/crypto/src/asn1/cms/Attributes.cs b/crypto/src/asn1/cms/Attributes.cs
index 41018453d..cdcada6dc 100644
--- a/crypto/src/asn1/cms/Attributes.cs
+++ b/crypto/src/asn1/cms/Attributes.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class Attributes
@@ -17,6 +19,21 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new Attributes(Asn1Set.GetInstance(taggedObject, declaredExplicit));
         }
 
+        public static Attributes GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is Attributes attributes)
+                return attributes;
+
+            Asn1Set asn1Set = Asn1Set.GetOptional(element);
+            if (asn1Set != null)
+                return new Attributes(asn1Set);
+
+            return null;
+        }
+
         private readonly Asn1Set m_attributes;
 
         private Attributes(Asn1Set attributes)
diff --git a/crypto/src/asn1/cms/AuthEnvelopedData.cs b/crypto/src/asn1/cms/AuthEnvelopedData.cs
index ae0380b64..62d54b33e 100644
--- a/crypto/src/asn1/cms/AuthEnvelopedData.cs
+++ b/crypto/src/asn1/cms/AuthEnvelopedData.cs
@@ -19,133 +19,80 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new AuthEnvelopedData(Asn1Sequence.GetInstance(obj, isExplicit));
         }
 
-        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 = DerInteger.Zero;
-
-			this.originatorInfo = originatorInfo;
-
-			// "There MUST be at least one element in the collection."
-			this.recipientInfos = recipientInfos;
-			if (this.recipientInfos.Count < 1)
-				throw new ArgumentException("AuthEnvelopedData requires at least 1 RecipientInfo");
-
-			this.authEncryptedContentInfo = authEncryptedContentInfo;
-
-			// "The authAttrs MUST be present if the content type carried in
-			// EncryptedContentInfo is not id-data."
-			this.authAttrs = authAttrs;
-			if (!authEncryptedContentInfo.ContentType.Equals(CmsObjectIdentifiers.Data))
-			{
-				if (authAttrs == null || authAttrs.Count < 1)
-					throw new ArgumentException("authAttrs must be present with non-data content");
-			}
-
-			this.mac = mac;
-
-			this.unauthAttrs = unauthAttrs;
-	    }
+        private readonly DerInteger m_version;
+        private readonly OriginatorInfo m_originatorInfo;
+        private readonly Asn1Set m_recipientInfos;
+        private readonly EncryptedContentInfo m_authEncryptedContentInfo;
+        private readonly Asn1Set m_authAttrs;
+        private readonly Asn1OctetString m_mac;
+        private readonly Asn1Set m_unauthAttrs;
+
+        public AuthEnvelopedData(OriginatorInfo originatorInfo, Asn1Set recipientInfos,
+			EncryptedContentInfo authEncryptedContentInfo, Asn1Set authAttrs, Asn1OctetString mac,
+			Asn1Set unauthAttrs)
+        {
+            m_version = DerInteger.Zero;
+			m_originatorInfo = originatorInfo;
+			m_recipientInfos = recipientInfos ?? throw new ArgumentNullException(nameof(recipientInfos));
+			m_authEncryptedContentInfo = authEncryptedContentInfo ?? throw new ArgumentNullException(nameof(authEncryptedContentInfo));
+            m_authAttrs = authAttrs;
+			m_mac = mac ?? throw new ArgumentNullException(nameof(mac));
+			m_unauthAttrs = unauthAttrs;
+
+            Validate();
+        }
 
-		private AuthEnvelopedData(Asn1Sequence seq)
+        private AuthEnvelopedData(Asn1Sequence seq)
 		{
-			int index = 0;
-
-			// "It MUST be set to 0."
-			Asn1Object tmp = seq[index++].ToAsn1Object();
-			version = DerInteger.GetInstance(tmp);
-			if (!version.HasValue(0))
-				throw new ArgumentException("AuthEnvelopedData version number must be 0");
-
-			tmp = seq[index++].ToAsn1Object();
-			if (tmp is Asn1TaggedObject taggedObject1)
-			{
-				originatorInfo = OriginatorInfo.GetInstance(taggedObject1, false);
-				tmp = seq[index++].ToAsn1Object();
-			}
-
-			// "There MUST be at least one element in the collection."
-			recipientInfos = Asn1Set.GetInstance(tmp);
-			if (recipientInfos.Count < 1)
-				throw new ArgumentException("AuthEnvelopedData requires at least 1 RecipientInfo");
-
-			tmp = seq[index++].ToAsn1Object();
-			authEncryptedContentInfo = EncryptedContentInfo.GetInstance(tmp);
-
-			tmp = seq[index++].ToAsn1Object();
-			if (tmp is Asn1TaggedObject taggedObject2)
-			{
-				authAttrs = Asn1Set.GetInstance(taggedObject2, false);
-				tmp = seq[index++].ToAsn1Object();
-			}
-			else
-			{
-				// "The authAttrs MUST be present if the content type carried in
-				// EncryptedContentInfo is not id-data."
-				if (!authEncryptedContentInfo.ContentType.Equals(CmsObjectIdentifiers.Data))
-				{
-					if (authAttrs == null || authAttrs.Count < 1)
-						throw new ArgumentException("authAttrs must be present with non-data content");
-				}
-			}
-
-			mac = Asn1OctetString.GetInstance(tmp);
-
-			if (seq.Count > index)
-			{
-				tmp = seq[index++].ToAsn1Object();
-				unauthAttrs = Asn1Set.GetInstance((Asn1TaggedObject)tmp, false);
-			}
+            int count = seq.Count, pos = 0;
+            if (count < 4 || count > 7)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_originatorInfo = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, OriginatorInfo.GetInstance);
+            m_recipientInfos = Asn1Set.GetInstance(seq[pos++]);
+            m_authEncryptedContentInfo = EncryptedContentInfo.GetInstance(seq[pos++]);
+            m_authAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, false, Asn1Set.GetInstance);
+            m_mac = Asn1OctetString.GetInstance(seq[pos++]);
+            m_unauthAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 2, false, Asn1Set.GetInstance);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
+
+            Validate();
 		}
 
-        public DerInteger Version
-		{
-			get { return version; }
-		}
+        private void Validate()
+        {
+            // "It MUST be set to 0."
+            if (!m_version.HasValue(0))
+                throw new ArgumentException("AuthEnvelopedData version number must be 0");
+
+            // "There MUST be at least one element in the collection."
+            if (m_recipientInfos.Count < 1)
+                throw new ArgumentException("AuthEnvelopedData requires at least 1 RecipientInfo");
+
+            // "The authAttrs MUST be present if the content type carried in EncryptedContentInfo is not id-data."
+            if (!CmsObjectIdentifiers.Data.Equals(m_authEncryptedContentInfo.ContentType))
+            {
+                if (m_authAttrs == null || m_authAttrs.Count < 1)
+                    throw new ArgumentException("authAttrs must be present with non-data content");
+            }
+        }
 
-		public OriginatorInfo OriginatorInfo
-		{
-			get { return originatorInfo; }
-		}
+        public DerInteger Version => m_version;
 
-		public Asn1Set RecipientInfos
-		{
-			get { return recipientInfos; }
-		}
+		public OriginatorInfo OriginatorInfo => m_originatorInfo;
 
-		public EncryptedContentInfo AuthEncryptedContentInfo
-		{
-			get { return authEncryptedContentInfo; }
-		}
+		public Asn1Set RecipientInfos => m_recipientInfos;
 
-		public Asn1Set AuthAttrs
-		{
-			get { return authAttrs; }
-		}
+		public EncryptedContentInfo AuthEncryptedContentInfo => m_authEncryptedContentInfo;
 
-		public Asn1OctetString Mac
-		{
-			get { return mac; }
-		}
+		public Asn1Set AuthAttrs => m_authAttrs;
 
-		public Asn1Set UnauthAttrs
-		{
-			get { return unauthAttrs; }
-		}
+		public Asn1OctetString Mac => m_mac;
+
+		public Asn1Set UnauthAttrs => m_unauthAttrs;
 
 		/**
 		 * Produce an object suitable for an Asn1OutputStream.
@@ -162,19 +109,20 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 */
 	    public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(version);
-            v.AddOptionalTagged(false, 0, originatorInfo);
-			v.Add(recipientInfos, authEncryptedContentInfo);
+			Asn1EncodableVector v = new Asn1EncodableVector(7);
+			v.Add(m_version);
+            v.AddOptionalTagged(false, 0, m_originatorInfo);
+			v.Add(m_recipientInfos, m_authEncryptedContentInfo);
 
 			// "authAttrs optionally contains the authenticated attributes."
             // "AuthAttributes MUST be DER encoded, even if the rest of the
             // AuthEnvelopedData structure is BER encoded."
-            v.AddOptionalTagged(false, 1, authAttrs);
+            v.AddOptionalTagged(false, 1, m_authAttrs);
 
-            v.Add(mac);
+            v.Add(m_mac);
 
             // "unauthAttrs optionally contains the unauthenticated attributes."
-            v.AddOptionalTagged(false, 2, unauthAttrs);
+            v.AddOptionalTagged(false, 2, m_unauthAttrs);
 
             return new BerSequence(v);
 		}
diff --git a/crypto/src/asn1/cms/AuthenticatedData.cs b/crypto/src/asn1/cms/AuthenticatedData.cs
index 47db0fa16..fad7de35f 100644
--- a/crypto/src/asn1/cms/AuthenticatedData.cs
+++ b/crypto/src/asn1/cms/AuthenticatedData.cs
@@ -21,132 +21,73 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new AuthenticatedData(Asn1Sequence.GetInstance(obj, isExplicit));
         }
 
-        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 readonly DerInteger m_version;
+        private readonly OriginatorInfo m_originatorInfo;
+        private readonly Asn1Set m_recipientInfos;
+        private readonly AlgorithmIdentifier m_macAlgorithm;
+        private readonly AlgorithmIdentifier m_digestAlgorithm;
+        private readonly ContentInfo m_encapsulatedContentInfo;
+        private readonly Asn1Set m_authAttrs;
+        private readonly Asn1OctetString m_mac;
+        private readonly Asn1Set m_unauthAttrs;
+
+        public AuthenticatedData(OriginatorInfo originatorInfo, Asn1Set recipientInfos,
+			AlgorithmIdentifier macAlgorithm, AlgorithmIdentifier digestAlgorithm, ContentInfo encapsulatedContent,
+            Asn1Set authAttrs, Asn1OctetString mac, Asn1Set unauthAttrs)
+        {
+            if ((digestAlgorithm == null) != (authAttrs == null))
+                throw new ArgumentException("digestAlgorithm and authAttrs must be set together");
+
+            m_version = CalculateVersionField(originatorInfo);
+            m_originatorInfo = originatorInfo;
+            m_macAlgorithm = macAlgorithm ?? throw new ArgumentNullException(nameof(macAlgorithm));
+            m_digestAlgorithm = digestAlgorithm;
+            m_recipientInfos = recipientInfos ?? throw new ArgumentNullException(nameof(recipientInfos));
+            m_encapsulatedContentInfo = encapsulatedContent ?? throw new ArgumentNullException(nameof(encapsulatedContent));
+            m_authAttrs = authAttrs;
+            m_mac = mac ?? throw new ArgumentNullException(nameof(mac));
+            m_unauthAttrs = unauthAttrs;
+        }
 
-		private AuthenticatedData(Asn1Sequence seq)
+        private AuthenticatedData(Asn1Sequence seq)
 		{
-			int index = 0;
-
-			version = (DerInteger)seq[index++];
-
-			Asn1Encodable tmp = seq[index++];
-			if (tmp is Asn1TaggedObject taggedObject1)
-			{
-				originatorInfo = OriginatorInfo.GetInstance(taggedObject1, false);
-				tmp = seq[index++];
-			}
-
-			recipientInfos = Asn1Set.GetInstance(tmp);
-			macAlgorithm = AlgorithmIdentifier.GetInstance(seq[index++]);
-
-			tmp = seq[index++];
-			if (tmp is Asn1TaggedObject taggedObject2)
-			{
-				digestAlgorithm = AlgorithmIdentifier.GetInstance(taggedObject2, false);
-				tmp = seq[index++];
-			}
-
-			encapsulatedContentInfo = ContentInfo.GetInstance(tmp);
-
-			tmp = seq[index++];
-			if (tmp is Asn1TaggedObject taggedObject3)
-			{
-				authAttrs = Asn1Set.GetInstance(taggedObject3, false);
-				tmp = seq[index++];
-			}
-
-			mac = Asn1OctetString.GetInstance(tmp);
-
-			if (seq.Count > index)
-			{
-				unauthAttrs = Asn1Set.GetInstance((Asn1TaggedObject)seq[index], false);
-			}
+            int count = seq.Count, pos = 0;
+            if (count < 5 || count > 9)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_originatorInfo = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, OriginatorInfo.GetInstance);
+            m_recipientInfos = Asn1Set.GetInstance(seq[pos++]);
+            m_macAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_digestAlgorithm = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, false, AlgorithmIdentifier.GetInstance);
+            m_encapsulatedContentInfo = ContentInfo.GetInstance(seq[pos++]);
+            m_authAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 2, false, Asn1Set.GetInstance);
+            m_mac = Asn1OctetString.GetInstance(seq[pos++]);
+            m_unauthAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 3, false, Asn1Set.GetInstance);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 		}
 
-        public DerInteger Version
-		{
-			get { return version; }
-		}
+        public DerInteger Version => m_version;
 
-		public OriginatorInfo OriginatorInfo
-		{
-			get { return originatorInfo; }
-		}
+        public OriginatorInfo OriginatorInfo => m_originatorInfo;
 
-		public Asn1Set RecipientInfos
-		{
-			get { return recipientInfos; }
-		}
+        public Asn1Set RecipientInfos => m_recipientInfos;
 
-		public AlgorithmIdentifier MacAlgorithm
-		{
-			get { return macAlgorithm; }
-		}
+        public AlgorithmIdentifier MacAlgorithm => m_macAlgorithm;
 
-        public AlgorithmIdentifier DigestAlgorithm
-        {
-            get { return digestAlgorithm; }
-        }
+        public AlgorithmIdentifier DigestAlgorithm => m_digestAlgorithm;
 
-		public ContentInfo EncapsulatedContentInfo
-		{
-			get { return encapsulatedContentInfo; }
-		}
+        public ContentInfo EncapsulatedContentInfo => m_encapsulatedContentInfo;
 
-		public Asn1Set AuthAttrs
-		{
-			get { return authAttrs; }
-		}
+        public Asn1Set AuthAttrs => m_authAttrs;
 
-		public Asn1OctetString Mac
-		{
-			get { return mac; }
-		}
+        public Asn1OctetString Mac => m_mac;
 
-		public Asn1Set UnauthAttrs
-		{
-			get { return unauthAttrs; }
-		}
+        public Asn1Set UnauthAttrs => m_unauthAttrs;
 
-		/**
+        /**
 		 * Produce an object suitable for an Asn1OutputStream.
 		 * <pre>
 		 * AuthenticatedData ::= SEQUENCE {
@@ -167,55 +108,77 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 * MessageAuthenticationCode ::= OCTET STRING
 		 * </pre>
 		 */
-		public override Asn1Object ToAsn1Object()
-		{
-			Asn1EncodableVector v = new Asn1EncodableVector(version);
-            v.AddOptionalTagged(false, 0, originatorInfo);
-			v.Add(recipientInfos, macAlgorithm);
-            v.AddOptionalTagged(false, 1, digestAlgorithm);
-			v.Add(encapsulatedContentInfo);
-            v.AddOptionalTagged(false, 2, authAttrs);
-			v.Add(mac);
-            v.AddOptionalTagged(false, 3, unauthAttrs);
-			return new BerSequence(v);
-		}
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(9);
+            v.Add(m_version);
+            v.AddOptionalTagged(false, 0, m_originatorInfo);
+            v.Add(m_recipientInfos, m_macAlgorithm);
+            v.AddOptionalTagged(false, 1, m_digestAlgorithm);
+            v.Add(m_encapsulatedContentInfo);
+            v.AddOptionalTagged(false, 2, m_authAttrs);
+            v.Add(m_mac);
+            v.AddOptionalTagged(false, 3, m_unauthAttrs);
+            return new BerSequence(v);
+        }
+
+        public static int CalculateVersion(OriginatorInfo origInfo) => CalculateVersionField(origInfo).IntValueExact;
 
-		public static int CalculateVersion(OriginatorInfo origInfo)
+        private static DerInteger CalculateVersionField(OriginatorInfo originatorInfo)
 		{
-			if (origInfo == null)
-				return 0;
-
-			int ver = 0;
-
-			foreach (object obj in origInfo.Certificates)
-			{
-				if (obj is Asn1TaggedObject tag)
-				{
-					if (tag.TagNo == 2)
-					{
-						ver = 1;
-					}
-					else if (tag.TagNo == 3)
-					{
-						ver = 3;
-						break;
-					}
-				}
-			}
-
-			foreach (object obj in origInfo.Crls)
-			{
-				if (obj is Asn1TaggedObject tag)
-				{
-					if (tag.TagNo == 1)
-					{
-						ver = 3;
-						break;
-					}
-				}
-			}
-
-			return ver;
+            /*
+             * IF (originatorInfo is present) AND
+             *    ((any certificates with a type of other are present) OR
+             *    (any crls with a type of other are present))
+             * THEN version is 3
+             * ELSE
+             *    IF ((originatorInfo is present) AND
+             *       (any version 2 attribute certificates are present))
+             *    THEN version is 1
+             *    ELSE version is 0
+             */
+
+            if (originatorInfo != null)
+            {
+                var crls = originatorInfo.Crls;
+                if (crls != null)
+                {
+                    foreach (var element in crls)
+                    {
+                        var tagged = Asn1TaggedObject.GetOptional(element);
+                        if (tagged != null)
+                        {
+                            // RevocationInfoChoice.other
+                            if (tagged.HasContextTag(1))
+                                return DerInteger.Three;
+                        }
+                    }
+                }
+
+                var certs = originatorInfo.Certificates;
+                if (certs != null)
+                {
+                    bool anyV2AttrCerts = false;
+
+                    foreach (var element in certs)
+                    {
+                        var tagged = Asn1TaggedObject.GetOptional(element);
+                        if (tagged != null)
+                        {
+                            // CertificateChoices.other
+                            if (tagged.HasContextTag(3))
+                                return DerInteger.Three;
+
+                            // CertificateChoices.v2AttrCert
+                            anyV2AttrCerts = anyV2AttrCerts || tagged.HasContextTag(2);
+                        }
+                    }
+
+                    if (anyV2AttrCerts)
+                        return DerInteger.One;
+                }
+            }
+            return DerInteger.Zero;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cms/CcmParameters.cs b/crypto/src/asn1/cms/CcmParameters.cs
index bd725721b..6355f7f73 100644
--- a/crypto/src/asn1/cms/CcmParameters.cs
+++ b/crypto/src/asn1/cms/CcmParameters.cs
@@ -23,44 +23,39 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new CcmParameters(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private readonly byte[] m_nonce;
+        private readonly Asn1OctetString m_nonce;
         private readonly int m_icvLen;
 
         private CcmParameters(Asn1Sequence seq)
         {
-            int count = seq.Count;
+            int count = seq.Count, pos = 0;
             if (count < 1 || count > 2)
                 throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            m_nonce = Asn1OctetString.GetInstance(seq[0]).GetOctets();
+            m_nonce = Asn1OctetString.GetInstance(seq[pos++]);
+            DerInteger icvLen = Asn1Utilities.ReadOptional(seq, ref pos, DerInteger.GetOptional);
 
-            if (count > 1)
-            {
-                m_icvLen = DerInteger.GetInstance(seq[1]).IntValueExact;
-            }
-            else
-            {
-                m_icvLen = DefaultIcvLen;
-            }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
+
+            m_icvLen = icvLen == null ? DefaultIcvLen : icvLen.IntValueExact;
         }
 
         public CcmParameters(byte[] nonce, int icvLen)
         {
-            m_nonce = Arrays.Clone(nonce);
+            m_nonce = new DerOctetString(nonce);
             m_icvLen = icvLen;
         }
 
-        public byte[] GetNonce() => Arrays.Clone(m_nonce);
+        public byte[] GetNonce() => Arrays.Clone(m_nonce.GetOctets());
 
         public int IcvLen => m_icvLen;
 
         public override Asn1Object ToAsn1Object()
         {
-            var nonce = new DerOctetString(m_nonce);
-
             return m_icvLen == DefaultIcvLen
-                ?   new DerSequence(nonce)
-                :   new DerSequence(nonce, new DerInteger(m_icvLen));
+                ?  new DerSequence(m_nonce)
+                :  new DerSequence(m_nonce, new DerInteger(m_icvLen));
         }
     }
 }
diff --git a/crypto/src/asn1/cms/CmsAlgorithmProtection.cs b/crypto/src/asn1/cms/CmsAlgorithmProtection.cs
index cb7a23f84..9923b0259 100644
--- a/crypto/src/asn1/cms/CmsAlgorithmProtection.cs
+++ b/crypto/src/asn1/cms/CmsAlgorithmProtection.cs
@@ -40,26 +40,26 @@ namespace Org.BouncyCastle.Asn1.Cms
         public static readonly int Signature = 1;
         public static readonly int Mac = 2;
 
-        private readonly AlgorithmIdentifier digestAlgorithm;
-        private readonly AlgorithmIdentifier signatureAlgorithm;
-        private readonly AlgorithmIdentifier macAlgorithm;
+        private readonly AlgorithmIdentifier m_digestAlgorithm;
+        private readonly AlgorithmIdentifier m_signatureAlgorithm;
+        private readonly AlgorithmIdentifier m_macAlgorithm;
 
         public CmsAlgorithmProtection(AlgorithmIdentifier digestAlgorithm, int type, AlgorithmIdentifier algorithmIdentifier)
         {
-            if (digestAlgorithm == null || algorithmIdentifier == null)
-                throw new ArgumentException("AlgorithmIdentifiers cannot be null");
+            m_digestAlgorithm = digestAlgorithm ?? throw new ArgumentNullException(nameof(digestAlgorithm));
 
-            this.digestAlgorithm = digestAlgorithm;
+            if (algorithmIdentifier == null)
+                throw new ArgumentNullException(nameof(algorithmIdentifier));
 
             if (type == 1)
             {
-                this.signatureAlgorithm = algorithmIdentifier;
-                this.macAlgorithm = null;
+                m_signatureAlgorithm = algorithmIdentifier;
+                m_macAlgorithm = null;
             }
             else if (type == 2)
             {
-                this.signatureAlgorithm = null;
-                this.macAlgorithm = algorithmIdentifier;
+                m_signatureAlgorithm = null;
+                m_macAlgorithm = algorithmIdentifier;
             }
             else
             {
@@ -67,43 +67,34 @@ namespace Org.BouncyCastle.Asn1.Cms
             }
         }
 
-        private CmsAlgorithmProtection(Asn1Sequence sequence)
+        private CmsAlgorithmProtection(Asn1Sequence seq)
         {
-            if (sequence.Count != 2)
-                throw new ArgumentException("Sequence wrong size: One of signatureAlgorithm or macAlgorithm must be present");
+            int count = seq.Count, pos = 0;
 
-            this.digestAlgorithm = AlgorithmIdentifier.GetInstance(sequence[0]);
+            // RFC 6211 2. Exactly one of signatureAlgorithm or macAlgorithm SHALL be present.
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            Asn1TaggedObject tagged = Asn1TaggedObject.GetInstance(sequence[1]);
-            if (tagged.TagNo == 1)
-            {
-                this.signatureAlgorithm = AlgorithmIdentifier.GetInstance(tagged, false);
-                this.macAlgorithm = null;
-            }
-            else if (tagged.TagNo == 2)
-            {
-                this.signatureAlgorithm = null;
+            m_digestAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_signatureAlgorithm = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, false, AlgorithmIdentifier.GetInstance);
+            m_macAlgorithm = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 2, false, AlgorithmIdentifier.GetInstance);
 
-                this.macAlgorithm = AlgorithmIdentifier.GetInstance(tagged, false);
-            }
-            else
-            {
-                throw new ArgumentException("Unknown tag found: " + tagged.TagNo);
-            }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public AlgorithmIdentifier DigestAlgorithm => digestAlgorithm;
+        public AlgorithmIdentifier DigestAlgorithm => m_digestAlgorithm;
 
-        public AlgorithmIdentifier MacAlgorithm => macAlgorithm;
+        public AlgorithmIdentifier MacAlgorithm => m_macAlgorithm;
 
-        public AlgorithmIdentifier SignatureAlgorithm => signatureAlgorithm;
+        public AlgorithmIdentifier SignatureAlgorithm => m_signatureAlgorithm;
 
         public override Asn1Object ToAsn1Object()
         {
             Asn1EncodableVector v = new Asn1EncodableVector(3);
-            v.Add(digestAlgorithm);
-            v.AddOptionalTagged(false, 1, signatureAlgorithm);
-            v.AddOptionalTagged(false, 2, macAlgorithm);
+            v.Add(m_digestAlgorithm);
+            v.AddOptionalTagged(false, 1, m_signatureAlgorithm);
+            v.AddOptionalTagged(false, 2, m_macAlgorithm);
             return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cms/CompressedData.cs b/crypto/src/asn1/cms/CompressedData.cs
index 5ae71708d..a22e153ef 100644
--- a/crypto/src/asn1/cms/CompressedData.cs
+++ b/crypto/src/asn1/cms/CompressedData.cs
@@ -35,46 +35,36 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private DerInteger			version;
-        private AlgorithmIdentifier	compressionAlgorithm;
-        private ContentInfo			encapContentInfo;
+        private readonly DerInteger m_version;
+        private readonly AlgorithmIdentifier m_compressionAlgorithm;
+        private readonly ContentInfo m_encapContentInfo;
 
-		public CompressedData(
-            AlgorithmIdentifier	compressionAlgorithm,
-            ContentInfo			encapContentInfo)
+        public CompressedData(AlgorithmIdentifier compressionAlgorithm, ContentInfo encapContentInfo)
         {
-            this.version = DerInteger.Zero;
-            this.compressionAlgorithm = compressionAlgorithm;
-            this.encapContentInfo = encapContentInfo;
+            m_version = DerInteger.Zero;
+            m_compressionAlgorithm = compressionAlgorithm ?? throw new ArgumentNullException(nameof(compressionAlgorithm));
+            m_encapContentInfo = encapContentInfo ?? throw new ArgumentNullException(nameof(encapContentInfo));
         }
 
         [Obsolete("Use 'GetInstance' instead")]
-        public CompressedData(
-            Asn1Sequence seq)
+        public CompressedData(Asn1Sequence seq)
         {
-            this.version = (DerInteger) seq[0];
-            this.compressionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
-            this.encapContentInfo = ContentInfo.GetInstance(seq[2]);
+            int count = seq.Count;
+            if (count != 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[0]);
+            m_compressionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
+            m_encapContentInfo = ContentInfo.GetInstance(seq[2]);
         }
 
-        public DerInteger Version
-		{
-			get { return version; }
-		}
+        public DerInteger Version => m_version;
 
-		public AlgorithmIdentifier CompressionAlgorithmIdentifier
-		{
-			get { return compressionAlgorithm; }
-		}
+        public AlgorithmIdentifier CompressionAlgorithmIdentifier => m_compressionAlgorithm;
 
-		public ContentInfo EncapContentInfo
-		{
-			get { return encapContentInfo; }
-		}
+        public ContentInfo EncapContentInfo => m_encapContentInfo;
 
-		public override Asn1Object ToAsn1Object()
-        {
-			return new BerSequence(version, compressionAlgorithm, encapContentInfo);
-        }
+		public override Asn1Object ToAsn1Object() =>
+            new BerSequence(m_version, m_compressionAlgorithm, m_encapContentInfo);
     }
 }
diff --git a/crypto/src/asn1/cms/ContentInfo.cs b/crypto/src/asn1/cms/ContentInfo.cs
index 952884901..b440b06a9 100644
--- a/crypto/src/asn1/cms/ContentInfo.cs
+++ b/crypto/src/asn1/cms/ContentInfo.cs
@@ -19,64 +19,48 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new ContentInfo(Asn1Sequence.GetInstance(obj, isExplicit));
         }
 
-        private readonly DerObjectIdentifier	contentType;
-        private readonly Asn1Encodable			content;
+        private readonly DerObjectIdentifier m_contentType;
+        private readonly Asn1Encodable m_content;
 
-        private ContentInfo(
-            Asn1Sequence seq)
+        private ContentInfo(Asn1Sequence seq)
         {
-            if (seq.Count < 1 || seq.Count > 2)
-                throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
+            int count = seq.Count;
+            if (count < 1 || count > 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            contentType = (DerObjectIdentifier) seq[0];
+            m_contentType = DerObjectIdentifier.GetInstance(seq[0]);
 
             if (seq.Count > 1)
             {
-                Asn1TaggedObject tagged = Asn1TaggedObject.GetInstance(seq[1], Asn1Tags.ContextSpecific);
-                if (!tagged.IsExplicit() || tagged.TagNo != 0)
-                    throw new ArgumentException("Bad tag for 'content'", "seq");
-
-                content = tagged.GetExplicitBaseObject();
+                m_content = Asn1TaggedObject.GetInstance(seq[1], Asn1Tags.ContextSpecific, 0).GetExplicitBaseObject();
             }
         }
 
-        public ContentInfo(
-            DerObjectIdentifier	contentType,
-            Asn1Encodable		content)
+        public ContentInfo(DerObjectIdentifier contentType, Asn1Encodable content)
         {
-            this.contentType = contentType;
-            this.content = content;
+            // TODO[cms] Blocked by CmsSignedDataGenerator.GenerateCounterSigners transient usage of null here
+            //m_contentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
+            m_contentType = contentType;
+            m_content = content;
         }
 
-        public DerObjectIdentifier ContentType
-        {
-            get { return contentType; }
-        }
+        public DerObjectIdentifier ContentType => m_contentType;
 
-        public Asn1Encodable Content
-        {
-            get { return content; }
-        }
+        public Asn1Encodable Content => m_content;
 
         /**
          * Produce an object suitable for an Asn1OutputStream.
          * <pre>
          * ContentInfo ::= Sequence {
          *          contentType ContentType,
-         *          content
-         *          [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+         *          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);
+            return m_content == null
+                ?  new BerSequence(m_contentType)
+                :  new BerSequence(m_contentType, new BerTaggedObject(0, m_content));
         }
     }
 }
diff --git a/crypto/src/asn1/cms/EncryptedContentInfo.cs b/crypto/src/asn1/cms/EncryptedContentInfo.cs
index af697af5d..94a5c00c2 100644
--- a/crypto/src/asn1/cms/EncryptedContentInfo.cs
+++ b/crypto/src/asn1/cms/EncryptedContentInfo.cs
@@ -25,48 +25,38 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private DerObjectIdentifier	contentType;
-        private AlgorithmIdentifier	contentEncryptionAlgorithm;
-        private Asn1OctetString		encryptedContent;
+        private DerObjectIdentifier m_contentType;
+        private AlgorithmIdentifier m_contentEncryptionAlgorithm;
+        private Asn1OctetString m_encryptedContent;
 
-		public EncryptedContentInfo(
-            DerObjectIdentifier	contentType,
-            AlgorithmIdentifier	contentEncryptionAlgorithm,
-            Asn1OctetString		encryptedContent)
+        public EncryptedContentInfo(DerObjectIdentifier contentType, AlgorithmIdentifier contentEncryptionAlgorithm,
+            Asn1OctetString encryptedContent)
         {
-            this.contentType = contentType;
-            this.contentEncryptionAlgorithm = contentEncryptionAlgorithm;
-            this.encryptedContent = encryptedContent;
+            m_contentType = contentType ?? throw new ArgumentNullException(nameof(contentType));
+            m_contentEncryptionAlgorithm = contentEncryptionAlgorithm ?? throw new ArgumentNullException(nameof(contentEncryptionAlgorithm));
+            m_encryptedContent = encryptedContent;
         }
 
         [Obsolete("Use 'GetInstance' instead")]
-        public EncryptedContentInfo(
-            Asn1Sequence seq)
+        public EncryptedContentInfo(Asn1Sequence seq)
         {
-            contentType = (DerObjectIdentifier) seq[0];
-            contentEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
+            int count = seq.Count, pos = 0;
+            if (count < 2 || count > 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-			if (seq.Count > 2)
-            {
-                encryptedContent = Asn1OctetString.GetInstance(
-					(Asn1TaggedObject) seq[2], false);
-            }
-        }
+            m_contentType = DerObjectIdentifier.GetInstance(seq[pos++]);
+            m_contentEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_encryptedContent = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, Asn1OctetString.GetInstance);
 
-        public DerObjectIdentifier ContentType
-        {
-            get { return contentType; }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-		public AlgorithmIdentifier ContentEncryptionAlgorithm
-        {
-			get { return contentEncryptionAlgorithm; }
-        }
+        public DerObjectIdentifier ContentType => m_contentType;
 
-		public Asn1OctetString EncryptedContent
-        {
-			get { return encryptedContent; }
-        }
+        public AlgorithmIdentifier ContentEncryptionAlgorithm => m_contentEncryptionAlgorithm;
+
+        public Asn1OctetString EncryptedContent => m_encryptedContent;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -80,12 +70,12 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(
-				contentType, contentEncryptionAlgorithm);
+            Asn1EncodableVector v = new Asn1EncodableVector(3);
+            v.Add(m_contentType, m_contentEncryptionAlgorithm);
 
-			if (encryptedContent != null)
+			if (m_encryptedContent != null)
             {
-                v.Add(new BerTaggedObject(false, 0, encryptedContent));
+                v.Add(new BerTaggedObject(false, 0, m_encryptedContent));
             }
 
 			return new BerSequence(v);
diff --git a/crypto/src/asn1/cms/EncryptedData.cs b/crypto/src/asn1/cms/EncryptedData.cs
index 4136994e6..fe0ff2c23 100644
--- a/crypto/src/asn1/cms/EncryptedData.cs
+++ b/crypto/src/asn1/cms/EncryptedData.cs
@@ -19,58 +19,41 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new EncryptedData(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private readonly DerInteger				version;
-		private readonly EncryptedContentInfo	encryptedContentInfo;
-		private readonly Asn1Set				unprotectedAttrs;
+        private readonly DerInteger m_version;
+        private readonly EncryptedContentInfo m_encryptedContentInfo;
+        private readonly Asn1Set m_unprotectedAttrs;
 
-		public EncryptedData(
-			EncryptedContentInfo encInfo)
-			: this(encInfo, null)
-		{
-		}
-
-		public EncryptedData(
-			EncryptedContentInfo	encInfo,
-			Asn1Set					unprotectedAttrs)
-		{
-			if (encInfo == null)
-				throw new ArgumentNullException("encInfo");
+        public EncryptedData(EncryptedContentInfo encInfo)
+            : this(encInfo, null)
+        {
+        }
 
-			this.version = new DerInteger((unprotectedAttrs == null) ? 0 : 2);
-			this.encryptedContentInfo = encInfo;
-			this.unprotectedAttrs = unprotectedAttrs;
+        public EncryptedData(EncryptedContentInfo encInfo, Asn1Set unprotectedAttrs)
+        {
+			m_version = unprotectedAttrs == null ? DerInteger.Zero : DerInteger.Two;
+			m_encryptedContentInfo = encInfo ?? throw new ArgumentNullException(nameof(encInfo));
+			m_unprotectedAttrs = unprotectedAttrs;
 		}
 
 		private EncryptedData(Asn1Sequence seq)
 		{
-			int count = seq.Count;
+			int count = seq.Count, pos = 0;
 			if (count < 2 || count > 3)
 				throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-			int pos = 0;
-
-			this.version = DerInteger.GetInstance(seq[pos++]);
-			this.encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[pos++]);
-			this.unprotectedAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, Asn1Set.GetInstance);
+			m_version = DerInteger.GetInstance(seq[pos++]);
+			m_encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[pos++]);
+			m_unprotectedAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, Asn1Set.GetInstance);
 
             if (pos != count)
                 throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public virtual DerInteger Version
-		{
-			get { return version; }
-		}
+		public virtual DerInteger Version => m_version;
 
-		public virtual EncryptedContentInfo EncryptedContentInfo
-		{
-			get { return encryptedContentInfo; }
-		}
+		public virtual EncryptedContentInfo EncryptedContentInfo => m_encryptedContentInfo;
 
-		public virtual Asn1Set UnprotectedAttrs
-		{
-			get { return unprotectedAttrs; }
-		}
+		public virtual Asn1Set UnprotectedAttrs => m_unprotectedAttrs;
 
 		/**
 		* <pre>
@@ -83,11 +66,12 @@ namespace Org.BouncyCastle.Asn1.Cms
 		*/
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(version, encryptedContentInfo);
+			Asn1EncodableVector v = new Asn1EncodableVector(3);
+			v.Add(m_version, m_encryptedContentInfo);
 
-			if (unprotectedAttrs != null)
+			if (m_unprotectedAttrs != null)
 			{
-				v.Add(new BerTaggedObject(false, 1, unprotectedAttrs));
+				v.Add(new BerTaggedObject(false, 1, m_unprotectedAttrs));
 			}
 
 			return new BerSequence(v);
diff --git a/crypto/src/asn1/cms/EnvelopedData.cs b/crypto/src/asn1/cms/EnvelopedData.cs
index 1b88d7791..846062f1c 100644
--- a/crypto/src/asn1/cms/EnvelopedData.cs
+++ b/crypto/src/asn1/cms/EnvelopedData.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class EnvelopedData
@@ -17,85 +19,53 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new EnvelopedData(Asn1Sequence.GetInstance(obj, explicitly));
         }
 
-        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)
+        private readonly DerInteger m_version;
+        private readonly OriginatorInfo m_originatorInfo;
+        private readonly Asn1Set m_recipientInfos;
+        private readonly EncryptedContentInfo m_encryptedContentInfo;
+        private readonly Asn1Set m_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;
+            m_version = CalculateVersionField(originatorInfo, recipientInfos, unprotectedAttrs);
+            m_originatorInfo = originatorInfo;
+            m_recipientInfos = recipientInfos ?? throw new ArgumentNullException(nameof(recipientInfos));
+            m_encryptedContentInfo = encryptedContentInfo ?? throw new ArgumentNullException(nameof(encryptedContentInfo));
+            m_unprotectedAttrs = unprotectedAttrs;
         }
 
-        public EnvelopedData(
-            OriginatorInfo originatorInfo,
-            Asn1Set recipientInfos,
-            EncryptedContentInfo encryptedContentInfo,
-            Attributes unprotectedAttrs)
+        public EnvelopedData(OriginatorInfo originatorInfo, Asn1Set recipientInfos,
+            EncryptedContentInfo encryptedContentInfo, Attributes unprotectedAttrs)
+            : this(originatorInfo, recipientInfos, encryptedContentInfo, Asn1Set.GetInstance(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);
         }
 
         private EnvelopedData(Asn1Sequence seq)
         {
-            int index = 0;
-
-            version = (DerInteger) seq[index++];
-
-            object tmp = seq[index++];
-
-            if (tmp is Asn1TaggedObject taggedObject)
-            {
-                originatorInfo = OriginatorInfo.GetInstance(taggedObject, false);
-                tmp = seq[index++];
-            }
-
-            recipientInfos = Asn1Set.GetInstance(tmp);
-            encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[index++]);
-
-            if (seq.Count > index)
-            {
-                unprotectedAttrs = Asn1Set.GetInstance((Asn1TaggedObject)seq[index], false);
-            }
+            int count = seq.Count, pos = 0;
+            if (count < 3 || count > 5)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_originatorInfo = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, OriginatorInfo.GetInstance);
+            m_recipientInfos = Asn1Set.GetInstance(seq[pos++]);
+            m_encryptedContentInfo = EncryptedContentInfo.GetInstance(seq[pos++]);
+            m_unprotectedAttrs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, false, Asn1Set.GetInstance);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public DerInteger Version
-        {
-            get { return version; }
-        }
+        public DerInteger Version => m_version;
 
-        public OriginatorInfo OriginatorInfo
-        {
-            get { return originatorInfo; }
-        }
+        public OriginatorInfo OriginatorInfo => m_originatorInfo;
 
-        public Asn1Set RecipientInfos
-        {
-            get { return recipientInfos; }
-        }
+        public Asn1Set RecipientInfos => m_recipientInfos;
 
-        public EncryptedContentInfo EncryptedContentInfo
-        {
-            get { return encryptedContentInfo; }
-        }
+        public EncryptedContentInfo EncryptedContentInfo => m_encryptedContentInfo;
 
-        public Asn1Set UnprotectedAttrs
-        {
-            get { return unprotectedAttrs; }
-        }
+        public Asn1Set UnprotectedAttrs => m_unprotectedAttrs;
 
         /**
          * Produce an object suitable for an Asn1OutputStream.
@@ -111,31 +81,104 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(version);
-            v.AddOptionalTagged(false, 0, originatorInfo);
-            v.Add(recipientInfos, encryptedContentInfo);
-            v.AddOptionalTagged(false, 1, unprotectedAttrs);
+            Asn1EncodableVector v = new Asn1EncodableVector(5);
+            v.Add(m_version);
+            v.AddOptionalTagged(false, 0, m_originatorInfo);
+            v.Add(m_recipientInfos, m_encryptedContentInfo);
+            v.AddOptionalTagged(false, 1, m_unprotectedAttrs);
             return new BerSequence(v);
         }
 
-        public static int CalculateVersion(OriginatorInfo originatorInfo, Asn1Set recipientInfos, Asn1Set unprotectedAttrs)
+        public static int CalculateVersion(OriginatorInfo originatorInfo, Asn1Set recipientInfos,
+            Asn1Set unprotectedAttrs)
         {
-            if (originatorInfo != null || unprotectedAttrs != null)
-            {
-                return 2;
-            }
+            return CalculateVersionField(originatorInfo, recipientInfos, unprotectedAttrs).IntValueExact;
+        }
 
-            foreach (object o in recipientInfos)
+        private static DerInteger CalculateVersionField(OriginatorInfo originatorInfo, Asn1Set recipientInfos,
+            Asn1Set unprotectedAttrs)
+        {
+            /*
+             * IF (originatorInfo is present) AND
+             *    ((any certificates with a type of other are present) OR
+             *    (any crls with a type of other are present))
+             * THEN version is 4
+             * ELSE
+             *    IF ((originatorInfo is present) AND
+             *       (any version 2 attribute certificates are present)) OR
+             *       (any RecipientInfo structures include pwri) OR
+             *       (any RecipientInfo structures include ori)
+             *    THEN version is 3
+             *    ELSE
+             *       IF (originatorInfo is absent) AND
+             *          (unprotectedAttrs is absent) AND
+             *          (all RecipientInfo structures are version 0)
+             *       THEN version is 0
+             *       ELSE version is 2
+             */
+
+            if (originatorInfo != null)
             {
-                RecipientInfo ri = RecipientInfo.GetInstance(o);
+                var crls = originatorInfo.Crls;
+                if (crls != null)
+                {
+                    foreach (var element in crls)
+                    {
+                        var tagged = Asn1TaggedObject.GetOptional(element);
+                        if (tagged != null)
+                        {
+                            // RevocationInfoChoice.other
+                            if (tagged.HasContextTag(1))
+                                return DerInteger.Four;
+                        }
+                    }
+                }
 
-                if (!ri.Version.HasValue(0))
+                var certs = originatorInfo.Certificates;
+                if (certs != null)
                 {
-                    return 2;
+                    bool anyV2AttrCerts = false;
+
+                    foreach (var element in certs)
+                    {
+                        var tagged = Asn1TaggedObject.GetOptional(element);
+                        if (tagged != null)
+                        {
+                            // CertificateChoices.other
+                            if (tagged.HasContextTag(3))
+                                return DerInteger.Four;
+
+                            // CertificateChoices.v2AttrCert
+                            anyV2AttrCerts = anyV2AttrCerts || tagged.HasContextTag(2);
+                        }
+                    }
+
+                    if (anyV2AttrCerts)
+                        return DerInteger.Three;
                 }
             }
 
-            return 0;
+            bool allV0Recipients = true;
+            foreach (var element in recipientInfos)
+            {
+                var recipientInfo = RecipientInfo.GetInstance(element);
+
+                // (any RecipientInfo structures include pwri) OR
+                // (any RecipientInfo structures include ori)
+                if (recipientInfo.IsPasswordOrOther())
+                    return DerInteger.Three;
+
+                // (all RecipientInfo structures are version 0)
+                // -- 'kari.version' is always 3
+                // -- 'kekri.version' is always 4
+                // -- 'pwri' and 'ori' have already been excluded
+                allV0Recipients = allV0Recipients && recipientInfo.IsKeyTransV0();
+            }
+
+            if (originatorInfo == null && unprotectedAttrs == null && allV0Recipients)
+                return DerInteger.Zero;
+
+            return DerInteger.Two;
         }
     }
 }
diff --git a/crypto/src/asn1/cms/Evidence.cs b/crypto/src/asn1/cms/Evidence.cs
index baa93d6b9..f3687009a 100644
--- a/crypto/src/asn1/cms/Evidence.cs
+++ b/crypto/src/asn1/cms/Evidence.cs
@@ -31,12 +31,12 @@ namespace Org.BouncyCastle.Asn1.Cms
 
         public Evidence(TimeStampTokenEvidence tstEvidence)
 		{
-			m_tstEvidence = tstEvidence;
+			m_tstEvidence = tstEvidence ?? throw new ArgumentNullException(nameof(tstEvidence));
 		}
 
         public Evidence(EvidenceRecord ersEvidence)
         {
-            m_ersEvidence = ersEvidence;
+            m_ersEvidence = ersEvidence ?? throw new ArgumentNullException(nameof(ersEvidence));
         }
 
         private Evidence(Asn1TaggedObject tagged)
diff --git a/crypto/src/asn1/cms/GcmParameters.cs b/crypto/src/asn1/cms/GcmParameters.cs
index 06b4b88ca..b97ba6806 100644
--- a/crypto/src/asn1/cms/GcmParameters.cs
+++ b/crypto/src/asn1/cms/GcmParameters.cs
@@ -23,44 +23,39 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new GcmParameters(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private readonly byte[] m_nonce;
+        private readonly Asn1OctetString m_nonce;
         private readonly int m_icvLen;
 
         private GcmParameters(Asn1Sequence seq)
         {
-            int count = seq.Count;
+            int count = seq.Count, pos = 0;
             if (count < 1 || count > 2)
                 throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            m_nonce = Asn1OctetString.GetInstance(seq[0]).GetOctets();
+            m_nonce = Asn1OctetString.GetInstance(seq[pos++]);
+            DerInteger icvLen = Asn1Utilities.ReadOptional(seq, ref pos, DerInteger.GetOptional);
 
-            if (count > 1)
-            {
-                m_icvLen = DerInteger.GetInstance(seq[1]).IntValueExact;
-            }
-            else
-            {
-                m_icvLen = DefaultIcvLen;
-            }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
+
+            m_icvLen = icvLen == null ? DefaultIcvLen : icvLen.IntValueExact;
         }
 
         public GcmParameters(byte[] nonce, int icvLen)
         {
-            m_nonce = Arrays.Clone(nonce);
+            m_nonce = new DerOctetString(nonce);
             m_icvLen = icvLen;
         }
 
-        public byte[] GetNonce() => Arrays.Clone(m_nonce);
+        public byte[] GetNonce() => Arrays.Clone(m_nonce.GetOctets());
 
         public int IcvLen => m_icvLen;
 
         public override Asn1Object ToAsn1Object()
         {
-            var nonce = new DerOctetString(m_nonce);
-
             return m_icvLen == DefaultIcvLen
-                ?   new DerSequence(nonce)
-                :   new DerSequence(nonce, new DerInteger(m_icvLen));
+                ?  new DerSequence(m_nonce)
+                :  new DerSequence(m_nonce, new DerInteger(m_icvLen));
         }
     }
 }
diff --git a/crypto/src/asn1/cms/IssuerAndSerialNumber.cs b/crypto/src/asn1/cms/IssuerAndSerialNumber.cs
index dcd489336..f88652def 100644
--- a/crypto/src/asn1/cms/IssuerAndSerialNumber.cs
+++ b/crypto/src/asn1/cms/IssuerAndSerialNumber.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Math;
 
@@ -25,6 +27,10 @@ namespace Org.BouncyCastle.Asn1.Cms
 
         private IssuerAndSerialNumber(Asn1Sequence seq)
         {
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
             m_name = X509Name.GetInstance(seq[0]);
             m_serialNumber = DerInteger.GetInstance(seq[1]);
         }
diff --git a/crypto/src/asn1/cms/KEKIdentifier.cs b/crypto/src/asn1/cms/KEKIdentifier.cs
index 433157ef3..cde145f04 100644
--- a/crypto/src/asn1/cms/KEKIdentifier.cs
+++ b/crypto/src/asn1/cms/KEKIdentifier.cs
@@ -23,62 +23,37 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private Asn1OctetString		keyIdentifier;
-        private Asn1GeneralizedTime date;
-        private OtherKeyAttribute	other;
+        private readonly Asn1OctetString m_keyIdentifier;
+        private readonly Asn1GeneralizedTime m_date;
+        private readonly OtherKeyAttribute m_other;
 
-		public KekIdentifier(
-            byte[]              keyIdentifier,
-            Asn1GeneralizedTime date,
-            OtherKeyAttribute   other)
+        public KekIdentifier(byte[] keyIdentifier, Asn1GeneralizedTime date, OtherKeyAttribute other)
         {
-            this.keyIdentifier = new DerOctetString(keyIdentifier);
-            this.date = date;
-            this.other = other;
+            m_keyIdentifier = new DerOctetString(keyIdentifier);
+            m_date = date;
+            m_other = other;
         }
 
         [Obsolete("Use 'GetInstance' instead")]
         public KekIdentifier(Asn1Sequence seq)
         {
-            keyIdentifier = (Asn1OctetString)seq[0];
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-			switch (seq.Count)
-            {
-            case 1:
-				break;
-            case 2:
-				if (seq[1] is Asn1GeneralizedTime asn1GeneralizedTime)
-				{
-					date = asn1GeneralizedTime;
-				}
-				else
-				{
-					other = OtherKeyAttribute.GetInstance(seq[2]);
-				}
-				break;
-            case 3:
-				date = (Asn1GeneralizedTime)seq[1];
-				other = OtherKeyAttribute.GetInstance(seq[2]);
-				break;
-            default:
-				throw new ArgumentException("Invalid KekIdentifier");
-            }
+            m_keyIdentifier = Asn1OctetString.GetInstance(seq[pos++]);
+            m_date = Asn1Utilities.ReadOptional(seq, ref pos, Asn1GeneralizedTime.GetOptional);
+            m_other = Asn1Utilities.ReadOptional(seq, ref pos, OtherKeyAttribute.GetOptional);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public Asn1OctetString KeyIdentifier
-		{
-			get { return keyIdentifier; }
-		}
+        public Asn1OctetString KeyIdentifier => m_keyIdentifier;
 
-		public Asn1GeneralizedTime Date
-		{
-			get { return date; }
-		}
+        public Asn1GeneralizedTime Date => m_date;
 
-		public OtherKeyAttribute Other
-		{
-			get { return other; }
-		}
+        public OtherKeyAttribute Other => m_other;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -92,8 +67,9 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(keyIdentifier);
-			v.AddOptional(date, other);
+            Asn1EncodableVector v = new Asn1EncodableVector(3);
+            v.Add(m_keyIdentifier);
+			v.AddOptional(m_date, m_other);
 			return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cms/KEKRecipientInfo.cs b/crypto/src/asn1/cms/KEKRecipientInfo.cs
index b1b85953e..ea1a2e080 100644
--- a/crypto/src/asn1/cms/KEKRecipientInfo.cs
+++ b/crypto/src/asn1/cms/KEKRecipientInfo.cs
@@ -25,51 +25,40 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private DerInteger			version;
-        private KekIdentifier       kekID;
-        private AlgorithmIdentifier keyEncryptionAlgorithm;
-        private Asn1OctetString     encryptedKey;
+        private readonly DerInteger m_version;
+        private readonly KekIdentifier m_kekID;
+        private readonly AlgorithmIdentifier m_keyEncryptionAlgorithm;
+        private readonly Asn1OctetString m_encryptedKey;
 
-		public KekRecipientInfo(
-            KekIdentifier       kekID,
-            AlgorithmIdentifier keyEncryptionAlgorithm,
-            Asn1OctetString     encryptedKey)
+        public KekRecipientInfo(KekIdentifier kekID, AlgorithmIdentifier keyEncryptionAlgorithm,
+            Asn1OctetString encryptedKey)
         {
-            this.version = DerInteger.Four;
-            this.kekID = kekID;
-            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-            this.encryptedKey = encryptedKey;
+            m_version = DerInteger.Four;
+            m_kekID = kekID ?? throw new ArgumentNullException(nameof(kekID));
+            m_keyEncryptionAlgorithm = keyEncryptionAlgorithm ?? throw new ArgumentNullException(nameof(keyEncryptionAlgorithm));
+            m_encryptedKey = encryptedKey ?? throw new ArgumentNullException(nameof(encryptedKey));
         }
 
         [Obsolete("Use 'GetInstance' instead")]
-        public KekRecipientInfo(
-            Asn1Sequence seq)
+        public KekRecipientInfo(Asn1Sequence seq)
         {
-            version = (DerInteger) seq[0];
-            kekID = KekIdentifier.GetInstance(seq[1]);
-            keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
-            encryptedKey = (Asn1OctetString) seq[3];
+            int count = seq.Count;
+            if (count != 4)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[0]);
+            m_kekID = KekIdentifier.GetInstance(seq[1]);
+            m_keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
+            m_encryptedKey = Asn1OctetString.GetInstance(seq[3]);
         }
 
-        public DerInteger Version
-		{
-			get { return version; }
-		}
+        public DerInteger Version => m_version;
 
-		public KekIdentifier KekID
-		{
-			get { return kekID; }
-		}
+        public KekIdentifier KekID => m_kekID;
 
-		public AlgorithmIdentifier KeyEncryptionAlgorithm
-		{
-			get { return keyEncryptionAlgorithm; }
-		}
+		public AlgorithmIdentifier KeyEncryptionAlgorithm => m_keyEncryptionAlgorithm;
 
-		public Asn1OctetString EncryptedKey
-		{
-			get { return encryptedKey; }
-		}
+        public Asn1OctetString EncryptedKey => m_encryptedKey;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -82,9 +71,7 @@ namespace Org.BouncyCastle.Asn1.Cms
          * }
          * </pre>
          */
-        public override Asn1Object ToAsn1Object()
-        {
-			return new DerSequence(version, kekID, keyEncryptionAlgorithm, encryptedKey);
-        }
+        public override Asn1Object ToAsn1Object() =>
+            new DerSequence(m_version, m_kekID, m_keyEncryptionAlgorithm, m_encryptedKey);
     }
 }
diff --git a/crypto/src/asn1/cms/KemRecipientInfo.cs b/crypto/src/asn1/cms/KemRecipientInfo.cs
index 4f771784a..ccfae719c 100644
--- a/crypto/src/asn1/cms/KemRecipientInfo.cs
+++ b/crypto/src/asn1/cms/KemRecipientInfo.cs
@@ -62,27 +62,25 @@ namespace Org.BouncyCastle.Asn1.Cms
 
         private KemRecipientInfo(Asn1Sequence seq)
         {
-            int count = seq.Count;
+            int count = seq.Count, pos = 0;
             if (count < 8 || count > 9)
                 throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-            int index = 0;
+            m_cmsVersion = DerInteger.GetInstance(seq[pos++]);
+            m_rid = RecipientIdentifier.GetInstance(seq[pos++]);
+            m_kem = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_kemct = Asn1OctetString.GetInstance(seq[pos++]);
+            m_kdf = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_kekLength = DerInteger.GetInstance(seq[pos++]);
+            m_ukm = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, Asn1OctetString.GetInstance);
+            m_wrap = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_encryptedKey = Asn1OctetString.GetInstance(seq[pos++]);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 
-            m_cmsVersion = DerInteger.GetInstance(seq[index++]);
             if (!m_cmsVersion.HasValue(0))
                 throw new ArgumentException("Unsupported version (hex): " + m_cmsVersion.Value.ToString(16));
-
-            m_rid = RecipientIdentifier.GetInstance(seq[index++]);
-            m_kem = AlgorithmIdentifier.GetInstance(seq[index++]);
-            m_kemct = Asn1OctetString.GetInstance(seq[index++]);
-            m_kdf = AlgorithmIdentifier.GetInstance(seq[index++]);
-            m_kekLength = DerInteger.GetInstance(seq[index++]);
-            m_ukm = Asn1Utilities.ReadOptionalContextTagged(seq, ref index, 0, true, Asn1OctetString.GetInstance);
-            m_wrap = AlgorithmIdentifier.GetInstance(seq[index++]);
-            m_encryptedKey = Asn1OctetString.GetInstance(seq[index++]);
-
-            if (index != count)
-                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
         public RecipientIdentifier RecipientIdentifier => m_rid;
diff --git a/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs b/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs
index 9e6e3bd5a..fb393b9ba 100644
--- a/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs
+++ b/crypto/src/asn1/cms/KeyAgreeRecipientIdentifier.cs
@@ -35,30 +35,22 @@ namespace Org.BouncyCastle.Asn1.Cms
             return Asn1Utilities.GetInstanceFromChoice(obj, isExplicit, GetInstance);
 		}
 
-		private readonly IssuerAndSerialNumber issuerSerial;
-		private readonly RecipientKeyIdentifier rKeyID;
+		private readonly IssuerAndSerialNumber m_issuerSerial;
+		private readonly RecipientKeyIdentifier m_rKeyID;
 
-		public KeyAgreeRecipientIdentifier(
-			IssuerAndSerialNumber issuerSerial)
-		{
-			this.issuerSerial = issuerSerial;
-		}
+        public KeyAgreeRecipientIdentifier(IssuerAndSerialNumber issuerSerial)
+        {
+            m_issuerSerial = issuerSerial ?? throw new ArgumentNullException(nameof(issuerSerial));
+        }
 
-		public KeyAgreeRecipientIdentifier(
-			RecipientKeyIdentifier rKeyID)
-		{
-			this.rKeyID = rKeyID;
-		}
+        public KeyAgreeRecipientIdentifier(RecipientKeyIdentifier rKeyID)
+        {
+            m_rKeyID = rKeyID ?? throw new ArgumentNullException(nameof(rKeyID));
+        }
 
-		public IssuerAndSerialNumber IssuerAndSerialNumber
-		{
-			get { return issuerSerial; }
-		}
+		public IssuerAndSerialNumber IssuerAndSerialNumber => m_issuerSerial;
 
-		public RecipientKeyIdentifier RKeyID
-		{
-			get { return rKeyID; }
-		}
+		public RecipientKeyIdentifier RKeyID => m_rKeyID;
 
 		/** 
 		 * Produce an object suitable for an Asn1OutputStream.
@@ -71,12 +63,10 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			if (issuerSerial != null)
-			{
-				return issuerSerial.ToAsn1Object();
-			}
+			if (m_issuerSerial != null)
+				return m_issuerSerial.ToAsn1Object();
 
-			return new DerTaggedObject(false, 0, rKeyID);
+			return new DerTaggedObject(false, 0, m_rKeyID);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs b/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs
index 345a45029..c1255adc9 100644
--- a/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs
+++ b/crypto/src/asn1/cms/KeyAgreeRecipientInfo.cs
@@ -25,68 +25,48 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        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)
+        private readonly DerInteger m_version;
+        private readonly OriginatorIdentifierOrKey m_originator;
+        private readonly Asn1OctetString m_ukm;
+        private readonly AlgorithmIdentifier m_keyEncryptionAlgorithm;
+        private readonly Asn1Sequence m_recipientEncryptedKeys;
+
+        public KeyAgreeRecipientInfo(OriginatorIdentifierOrKey originator, Asn1OctetString ukm,
+            AlgorithmIdentifier keyEncryptionAlgorithm, Asn1Sequence recipientEncryptedKeys)
         {
-            this.version = DerInteger.Three;
-            this.originator = originator;
-            this.ukm = ukm;
-            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-            this.recipientEncryptedKeys = recipientEncryptedKeys;
+            m_version = DerInteger.Three;
+            m_originator = originator ?? throw new ArgumentNullException(nameof(originator));
+            m_ukm = ukm;
+            m_keyEncryptionAlgorithm = keyEncryptionAlgorithm ?? throw new ArgumentNullException(nameof(keyEncryptionAlgorithm));
+            m_recipientEncryptedKeys = recipientEncryptedKeys ?? throw new ArgumentNullException(nameof(recipientEncryptedKeys));
         }
 
         [Obsolete("Use 'GetInstance' instead")]
         public KeyAgreeRecipientInfo(Asn1Sequence seq)
         {
-            int index = 0;
-
-            version = (DerInteger) seq[index++];
-            originator = OriginatorIdentifierOrKey.GetInstance((Asn1TaggedObject)seq[index++], true);
-
-			if (seq[index] is Asn1TaggedObject taggedObject)
-            {
-                ukm = Asn1OctetString.GetInstance(taggedObject, true);
-                ++index;
-            }
-
-			keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[index++]);
-
-			recipientEncryptedKeys = (Asn1Sequence)seq[index++];
+            int count = seq.Count, pos = 0;
+            if (count < 4 || count > 5)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_originator = Asn1Utilities.ReadContextTagged(seq, ref pos, 0, true, OriginatorIdentifierOrKey.GetInstance);
+            m_ukm = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, true, Asn1OctetString.GetInstance);
+            m_keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_recipientEncryptedKeys = Asn1Sequence.GetInstance(seq[pos++]);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public DerInteger Version
-		{
-			get { return version; }
-		}
+        public DerInteger Version => m_version;
 
-		public OriginatorIdentifierOrKey Originator
-		{
-			get { return originator; }
-		}
+        public OriginatorIdentifierOrKey Originator => m_originator;
 
-		public Asn1OctetString UserKeyingMaterial
-		{
-			get { return ukm; }
-		}
+        public Asn1OctetString UserKeyingMaterial => m_ukm;
 
-		public AlgorithmIdentifier KeyEncryptionAlgorithm
-		{
-			get { return keyEncryptionAlgorithm; }
-		}
+		public AlgorithmIdentifier KeyEncryptionAlgorithm => m_keyEncryptionAlgorithm;
 
-		public Asn1Sequence RecipientEncryptedKeys
-		{
-			get { return recipientEncryptedKeys; }
-		}
+        public Asn1Sequence RecipientEncryptedKeys => m_recipientEncryptedKeys;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -104,9 +84,10 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(version, new DerTaggedObject(true, 0, originator));
-            v.AddOptionalTagged(true, 1, ukm);
-			v.Add(keyEncryptionAlgorithm, recipientEncryptedKeys);
+            Asn1EncodableVector v = new Asn1EncodableVector(5);
+            v.Add(m_version, new DerTaggedObject(true, 0, m_originator));
+            v.AddOptionalTagged(true, 1, m_ukm);
+			v.Add(m_keyEncryptionAlgorithm, m_recipientEncryptedKeys);
 			return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cms/KeyTransRecipientInfo.cs b/crypto/src/asn1/cms/KeyTransRecipientInfo.cs
index 1d9825b02..2274d1631 100644
--- a/crypto/src/asn1/cms/KeyTransRecipientInfo.cs
+++ b/crypto/src/asn1/cms/KeyTransRecipientInfo.cs
@@ -1,7 +1,6 @@
 using System;
 
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cms
 {
@@ -26,59 +25,40 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private DerInteger          version;
-        private RecipientIdentifier rid;
-        private AlgorithmIdentifier keyEncryptionAlgorithm;
-        private Asn1OctetString     encryptedKey;
+        private readonly DerInteger m_version;
+        private readonly RecipientIdentifier m_rid;
+        private readonly AlgorithmIdentifier m_keyEncryptionAlgorithm;
+        private readonly Asn1OctetString m_encryptedKey;
 
-		public KeyTransRecipientInfo(
-            RecipientIdentifier rid,
-            AlgorithmIdentifier keyEncryptionAlgorithm,
-            Asn1OctetString     encryptedKey)
+        public KeyTransRecipientInfo(RecipientIdentifier rid, AlgorithmIdentifier keyEncryptionAlgorithm,
+            Asn1OctetString encryptedKey)
         {
-            if (rid.ToAsn1Object() is Asn1TaggedObject)
-            {
-                this.version = DerInteger.Two;
-            }
-            else
-            {
-                this.version = DerInteger.Zero;
-            }
-
-			this.rid = rid;
-            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-            this.encryptedKey = encryptedKey;
+            m_rid = rid ?? throw new ArgumentNullException(nameof(rid));
+            m_keyEncryptionAlgorithm = keyEncryptionAlgorithm ?? throw new ArgumentNullException(nameof(keyEncryptionAlgorithm));
+            m_encryptedKey = encryptedKey ?? throw new ArgumentNullException(nameof(encryptedKey));
+            m_version = rid.IsTagged ? DerInteger.Two : DerInteger.Zero;
         }
 
         [Obsolete("Use 'GetInstance' instead")]
-        public KeyTransRecipientInfo(
-            Asn1Sequence seq)
+        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];
+            int count = seq.Count;
+            if (count != 4)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[0]);
+            m_rid = RecipientIdentifier.GetInstance(seq[1]);
+            m_keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
+            m_encryptedKey = Asn1OctetString.GetInstance(seq[3]);
         }
 
-		public DerInteger Version
-		{
-			get { return version; }
-		}
+        public DerInteger Version => m_version;
 
-		public RecipientIdentifier RecipientIdentifier
-		{
-			get { return rid; }
-		}
+        public RecipientIdentifier RecipientIdentifier => m_rid;
 
-		public AlgorithmIdentifier KeyEncryptionAlgorithm
-		{
-			get { return keyEncryptionAlgorithm; }
-		}
+        public AlgorithmIdentifier KeyEncryptionAlgorithm => m_keyEncryptionAlgorithm;
 
-		public Asn1OctetString EncryptedKey
-		{
-			get { return encryptedKey; }
-		}
+        public Asn1OctetString EncryptedKey => m_encryptedKey;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -91,9 +71,7 @@ namespace Org.BouncyCastle.Asn1.Cms
          * }
          * </pre>
          */
-        public override Asn1Object ToAsn1Object()
-        {
-			return new DerSequence(version, rid, keyEncryptionAlgorithm, encryptedKey);
-        }
+        public override Asn1Object ToAsn1Object() =>
+			new DerSequence(m_version, m_rid, m_keyEncryptionAlgorithm, m_encryptedKey);
     }
 }
diff --git a/crypto/src/asn1/cms/MetaData.cs b/crypto/src/asn1/cms/MetaData.cs
index a0d783d59..5754fd6f1 100644
--- a/crypto/src/asn1/cms/MetaData.cs
+++ b/crypto/src/asn1/cms/MetaData.cs
@@ -19,46 +19,59 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new MetaData(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        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;
-		}
+        public static MetaData GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
 
-		private MetaData(Asn1Sequence seq)
-		{
-			this.hashProtected = DerBoolean.GetInstance(seq[0]);
-
-			int index = 1;
-
-			if (index < seq.Count && seq[index] is DerUtf8String utf8)
-			{
-				this.fileName = utf8;
-				++index;
-			}
-			if (index < seq.Count && seq[index] is DerIA5String ia5)
-			{
-				this.mediaType = ia5;
-                ++index;
-            }
-            if (index < seq.Count)
-			{
-				this.otherMetaData = Attributes.GetInstance(seq[index++]);
-			}
-		}
+            if (element is MetaData metaData)
+                return metaData;
+
+            Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
+            if (asn1Sequence != null)
+                return new MetaData(asn1Sequence);
+
+            return null;
+        }
+
+        private readonly DerBoolean m_hashProtected;
+        private readonly DerUtf8String m_fileName;
+        private readonly DerIA5String m_mediaType;
+        private readonly Attributes m_otherMetaData;
+
+        public MetaData(DerBoolean hashProtected, DerUtf8String fileName, DerIA5String mediaType,
+            Attributes otherMetaData)
+        {
+            m_hashProtected = hashProtected ?? throw new ArgumentNullException(nameof(hashProtected));
+            m_fileName = fileName;
+            m_mediaType = mediaType;
+            m_otherMetaData = otherMetaData;
+        }
+
+        private MetaData(Asn1Sequence seq)
+        {
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 4)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_hashProtected = DerBoolean.GetInstance(seq[pos++]);
+            m_fileName = Asn1Utilities.ReadOptional(seq, ref pos, DerUtf8String.GetOptional);
+            m_mediaType = Asn1Utilities.ReadOptional(seq, ref pos, DerIA5String.GetOptional);
+            m_otherMetaData = Asn1Utilities.ReadOptional(seq, ref pos, Attributes.GetOptional);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
+        }
 
-		/**
+        public virtual bool IsHashProtected => m_hashProtected.IsTrue;
+
+        public virtual DerUtf8String FileName => m_fileName;
+
+        public virtual DerIA5String MediaType => m_mediaType;
+
+        public virtual Attributes OtherMetaData => m_otherMetaData;
+
+        /**
 		 * <pre>
 		 * MetaData ::= SEQUENCE {
 		 *   hashProtected        BOOLEAN,
@@ -69,31 +82,12 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 * </pre>
 		 * @return
 		 */
-		public override Asn1Object ToAsn1Object()
+        public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(hashProtected);
-			v.AddOptional(fileName, mediaType, otherMetaData);
+            Asn1EncodableVector v = new Asn1EncodableVector(4);
+            v.Add(m_hashProtected);
+			v.AddOptional(m_fileName, m_mediaType, m_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
index 6d127ccc3..f5cc1f2f7 100644
--- a/crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs
+++ b/crypto/src/asn1/cms/OriginatorIdentifierOrKey.cs
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
         public OriginatorIdentifierOrKey(IssuerAndSerialNumber id)
         {
-            m_id = id;
+            m_id = id ?? throw new ArgumentNullException(nameof(id));
         }
 
         public OriginatorIdentifierOrKey(SubjectKeyIdentifier id)
diff --git a/crypto/src/asn1/cms/OriginatorInfo.cs b/crypto/src/asn1/cms/OriginatorInfo.cs
index 48b771bcb..74569cbec 100644
--- a/crypto/src/asn1/cms/OriginatorInfo.cs
+++ b/crypto/src/asn1/cms/OriginatorInfo.cs
@@ -1,7 +1,5 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class OriginatorInfo
@@ -25,57 +23,32 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private Asn1Set certs;
-        private Asn1Set crls;
+        private readonly Asn1Set m_certs;
+        private readonly Asn1Set m_crls;
 
-        public OriginatorInfo(
-            Asn1Set certs,
-            Asn1Set crls)
+        public OriginatorInfo(Asn1Set certs, Asn1Set crls)
         {
-            this.certs = certs;
-            this.crls = crls;
+            m_certs = certs;
+            m_crls = crls;
         }
 
         [Obsolete("Use 'GetInstance' instead")]
-        public OriginatorInfo(
-            Asn1Sequence seq)
+        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");
-            }
+            int count = seq.Count, pos = 0;
+            if (count < 0 || count > 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_certs = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, Asn1Set.GetInstance);
+            m_crls = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, false, Asn1Set.GetInstance);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public Asn1Set Certificates
-		{
-			get { return certs; }
-		}
+        public Asn1Set Certificates => m_certs;
 
-		public Asn1Set Crls
-		{
-			get { return crls; }
-		}
+        public Asn1Set Crls => m_crls;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -89,8 +62,8 @@ namespace Org.BouncyCastle.Asn1.Cms
         public override Asn1Object ToAsn1Object()
         {
             Asn1EncodableVector v = new Asn1EncodableVector(2);
-            v.AddOptionalTagged(false, 0, certs);
-            v.AddOptionalTagged(false, 1, crls);
+            v.AddOptionalTagged(false, 0, m_certs);
+            v.AddOptionalTagged(false, 1, m_crls);
 			return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cms/OriginatorPublicKey.cs b/crypto/src/asn1/cms/OriginatorPublicKey.cs
index 6ed017877..eb302c00b 100644
--- a/crypto/src/asn1/cms/OriginatorPublicKey.cs
+++ b/crypto/src/asn1/cms/OriginatorPublicKey.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Asn1.X509;
 
 namespace Org.BouncyCastle.Asn1.Cms
@@ -29,12 +31,16 @@ namespace Org.BouncyCastle.Asn1.Cms
 
         public OriginatorPublicKey(AlgorithmIdentifier algorithm, DerBitString publicKey)
         {
-            m_algorithm = algorithm;
-            m_publicKey = publicKey;
+            m_algorithm = algorithm ?? throw new ArgumentNullException(nameof(algorithm));
+            m_publicKey = publicKey ?? throw new ArgumentNullException(nameof(publicKey));
         }
 
         private OriginatorPublicKey(Asn1Sequence seq)
         {
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
             m_algorithm = AlgorithmIdentifier.GetInstance(seq[0]);
             m_publicKey = DerBitString.GetInstance(seq[1]);
         }
diff --git a/crypto/src/asn1/cms/OtherKeyAttribute.cs b/crypto/src/asn1/cms/OtherKeyAttribute.cs
index 52f013950..901009209 100644
--- a/crypto/src/asn1/cms/OtherKeyAttribute.cs
+++ b/crypto/src/asn1/cms/OtherKeyAttribute.cs
@@ -23,34 +23,46 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private DerObjectIdentifier	keyAttrId;
-        private Asn1Encodable		keyAttr;
+        public static OtherKeyAttribute GetOptional(Asn1Encodable element)
+        {
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is OtherKeyAttribute otherKeyAttribute)
+                return otherKeyAttribute;
+
+            Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
+            if (asn1Sequence != null)
+#pragma warning disable CS0618 // Type or member is obsolete
+                return new OtherKeyAttribute(asn1Sequence);
+#pragma warning restore CS0618 // Type or member is obsolete
+
+            return null;
+        }
+
+        private readonly DerObjectIdentifier m_keyAttrId;
+        private readonly Asn1Encodable m_keyAttr;
 
         [Obsolete("Use 'GetInstance' instead")]
-        public OtherKeyAttribute(
-            Asn1Sequence seq)
+        public OtherKeyAttribute(Asn1Sequence seq)
         {
-            keyAttrId = (DerObjectIdentifier) seq[0];
-            keyAttr = seq[1];
+            int count = seq.Count;
+            if (count < 1 || count > 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_keyAttrId = DerObjectIdentifier.GetInstance(seq[0]);
+            m_keyAttr = count == 1 ? null : seq[1];
         }
 
-		public OtherKeyAttribute(
-            DerObjectIdentifier	keyAttrId,
-            Asn1Encodable		keyAttr)
+        public OtherKeyAttribute(DerObjectIdentifier keyAttrId, Asn1Encodable keyAttr)
         {
-            this.keyAttrId = keyAttrId;
-            this.keyAttr = keyAttr;
+            m_keyAttrId = keyAttrId ?? throw new ArgumentNullException(nameof(keyAttrId));
+            m_keyAttr = keyAttr;
         }
 
-		public DerObjectIdentifier KeyAttrId
-		{
-			get { return keyAttrId; }
-		}
+        public DerObjectIdentifier KeyAttrId => m_keyAttrId;
 
-		public Asn1Encodable KeyAttr
-		{
-			get { return keyAttr; }
-		}
+        public Asn1Encodable KeyAttr => m_keyAttr;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -63,7 +75,9 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-			return new DerSequence(keyAttrId, keyAttr);
+            return m_keyAttr == null
+                ?  new DerSequence(m_keyAttrId)
+                :  new DerSequence(m_keyAttrId, m_keyAttr);
         }
     }
 }
diff --git a/crypto/src/asn1/cms/OtherRecipientInfo.cs b/crypto/src/asn1/cms/OtherRecipientInfo.cs
index 989b6183e..b47a256f9 100644
--- a/crypto/src/asn1/cms/OtherRecipientInfo.cs
+++ b/crypto/src/asn1/cms/OtherRecipientInfo.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class OtherRecipientInfo
@@ -17,32 +19,28 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new OtherRecipientInfo(Asn1Sequence.GetInstance(obj, explicitly));
         }
 
-        private readonly DerObjectIdentifier oriType;
-        private readonly Asn1Encodable oriValue;
+        private readonly DerObjectIdentifier m_oriType;
+        private readonly Asn1Encodable m_oriValue;
 
-        public OtherRecipientInfo(
-            DerObjectIdentifier	oriType,
-            Asn1Encodable		oriValue)
+        public OtherRecipientInfo(DerObjectIdentifier oriType, Asn1Encodable oriValue)
         {
-            this.oriType = oriType;
-            this.oriValue = oriValue;
+            m_oriType = oriType ?? throw new ArgumentNullException(nameof(oriType));
+            m_oriValue = oriValue ?? throw new ArgumentNullException(nameof(oriValue));
         }
 
         private OtherRecipientInfo(Asn1Sequence seq)
         {
-            oriType = DerObjectIdentifier.GetInstance(seq[0]);
-            oriValue = seq[1];
-        }
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-        public virtual DerObjectIdentifier OriType
-        {
-            get { return oriType; }
+            m_oriType = DerObjectIdentifier.GetInstance(seq[0]);
+            m_oriValue = seq[1];
         }
 
-        public virtual Asn1Encodable OriValue
-        {
-            get { return oriValue; }
-        }
+        public virtual DerObjectIdentifier OriType => m_oriType;
+
+        public virtual Asn1Encodable OriValue => m_oriValue;
 
         /**
          * Produce an object suitable for an Asn1OutputStream.
@@ -52,9 +50,6 @@ namespace Org.BouncyCastle.Asn1.Cms
          *    oriValue ANY DEFINED BY oriType }
          * </pre>
          */
-        public override Asn1Object ToAsn1Object()
-        {
-            return new DerSequence(oriType, oriValue);
-        }
+        public override Asn1Object ToAsn1Object() => new DerSequence(m_oriType, m_oriValue);
     }
 }
diff --git a/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
index d39f14aa8..b85468beb 100644
--- a/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
+++ b/crypto/src/asn1/cms/OtherRevocationInfoFormat.cs
@@ -1,4 +1,6 @@
-namespace Org.BouncyCastle.Asn1.Cms
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
 {
     public class OtherRevocationInfoFormat
         : Asn1Encodable
@@ -17,32 +19,28 @@
             return new OtherRevocationInfoFormat(Asn1Sequence.GetInstance(obj, isExplicit));
         }
 
-        private readonly DerObjectIdentifier otherRevInfoFormat;
-        private readonly Asn1Encodable otherRevInfo;
+        private readonly DerObjectIdentifier m_otherRevInfoFormat;
+        private readonly Asn1Encodable m_otherRevInfo;
 
-        public OtherRevocationInfoFormat(
-            DerObjectIdentifier otherRevInfoFormat,
-            Asn1Encodable otherRevInfo)
+        public OtherRevocationInfoFormat(DerObjectIdentifier otherRevInfoFormat, Asn1Encodable otherRevInfo)
         {
-            this.otherRevInfoFormat = otherRevInfoFormat;
-            this.otherRevInfo = otherRevInfo;
+            m_otherRevInfoFormat = otherRevInfoFormat ?? throw new ArgumentNullException(nameof(otherRevInfoFormat));
+            m_otherRevInfo = otherRevInfo ?? throw new ArgumentNullException(nameof(otherRevInfo));
         }
 
         private OtherRevocationInfoFormat(Asn1Sequence seq)
         {
-            otherRevInfoFormat = DerObjectIdentifier.GetInstance(seq[0]);
-            otherRevInfo = seq[1];
-        }
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-        public virtual DerObjectIdentifier InfoFormat
-        {
-            get { return otherRevInfoFormat; }
+            m_otherRevInfoFormat = DerObjectIdentifier.GetInstance(seq[0]);
+            m_otherRevInfo = seq[1];
         }
 
-        public virtual Asn1Encodable Info
-        {
-            get { return otherRevInfo; }
-        }
+        public virtual DerObjectIdentifier InfoFormat => m_otherRevInfoFormat;
+
+        public virtual Asn1Encodable Info => m_otherRevInfo;
 
         /** 
          * Produce an object suitable for an ASN1OutputStream.
@@ -52,9 +50,6 @@
          *      otherRevInfo ANY DEFINED BY otherRevInfoFormat }
          * </pre>
          */
-        public override Asn1Object ToAsn1Object()
-        {
-            return new DerSequence(otherRevInfoFormat, otherRevInfo);
-        }
+        public override Asn1Object ToAsn1Object() => new DerSequence(m_otherRevInfoFormat, m_otherRevInfo);
     }
 }
diff --git a/crypto/src/asn1/cms/PasswordRecipientInfo.cs b/crypto/src/asn1/cms/PasswordRecipientInfo.cs
index 5fac434ca..41ab686ef 100644
--- a/crypto/src/asn1/cms/PasswordRecipientInfo.cs
+++ b/crypto/src/asn1/cms/PasswordRecipientInfo.cs
@@ -25,68 +25,46 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private readonly DerInteger				version;
-        private readonly AlgorithmIdentifier	keyDerivationAlgorithm;
-        private readonly AlgorithmIdentifier	keyEncryptionAlgorithm;
-        private readonly Asn1OctetString		encryptedKey;
+        private readonly DerInteger m_version;
+        private readonly AlgorithmIdentifier m_keyDerivationAlgorithm;
+        private readonly AlgorithmIdentifier m_keyEncryptionAlgorithm;
+        private readonly Asn1OctetString m_encryptedKey;
 
-		public PasswordRecipientInfo(
-            AlgorithmIdentifier	keyEncryptionAlgorithm,
-            Asn1OctetString		encryptedKey)
+        public PasswordRecipientInfo(AlgorithmIdentifier keyEncryptionAlgorithm, Asn1OctetString encryptedKey)
+            : this(keyDerivationAlgorithm: null, keyEncryptionAlgorithm, encryptedKey)
         {
-            this.version = DerInteger.Zero;
-            this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-            this.encryptedKey = encryptedKey;
         }
 
-		public PasswordRecipientInfo(
-			AlgorithmIdentifier	keyDerivationAlgorithm,
-			AlgorithmIdentifier	keyEncryptionAlgorithm,
-			Asn1OctetString		encryptedKey)
+		public PasswordRecipientInfo(AlgorithmIdentifier keyDerivationAlgorithm,
+            AlgorithmIdentifier keyEncryptionAlgorithm, Asn1OctetString encryptedKey)
 		{
-			this.version = DerInteger.Zero;
-			this.keyDerivationAlgorithm = keyDerivationAlgorithm;
-			this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
-			this.encryptedKey = encryptedKey;
+            m_version = DerInteger.Zero;
+            m_keyDerivationAlgorithm = keyDerivationAlgorithm;
+            m_keyEncryptionAlgorithm = keyEncryptionAlgorithm ?? throw new ArgumentNullException(nameof(keyEncryptionAlgorithm));
+            m_encryptedKey = encryptedKey ?? throw new ArgumentNullException(nameof(encryptedKey));
 		}
 
         [Obsolete("Use 'GetInstance' instead")]
         public PasswordRecipientInfo(Asn1Sequence seq)
         {
-            version = (DerInteger)seq[0];
+            int count = seq.Count, pos = 0;
+            if (count < 3 || count > 4)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-			if (seq[1] is Asn1TaggedObject taggedObject)
-            {
-                keyDerivationAlgorithm = AlgorithmIdentifier.GetInstance(taggedObject, false);
-                keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[2]);
-                encryptedKey = (Asn1OctetString)seq[3];
-            }
-            else
-            {
-                keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[1]);
-                encryptedKey = (Asn1OctetString)seq[2];
-            }
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_keyDerivationAlgorithm = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false,
+                AlgorithmIdentifier.GetInstance);
+            m_keyEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_encryptedKey = Asn1OctetString.GetInstance(seq[pos++]);
         }
 
-        public DerInteger Version
-		{
-			get { return version; }
-		}
+        public DerInteger Version => m_version;
 
-		public AlgorithmIdentifier KeyDerivationAlgorithm
-		{
-			get { return keyDerivationAlgorithm; }
-		}
+        public AlgorithmIdentifier KeyDerivationAlgorithm => m_keyDerivationAlgorithm;
 
-		public AlgorithmIdentifier KeyEncryptionAlgorithm
-		{
-			get { return keyEncryptionAlgorithm; }
-		}
+        public AlgorithmIdentifier KeyEncryptionAlgorithm => m_keyEncryptionAlgorithm;
 
-		public Asn1OctetString EncryptedKey
-		{
-			get { return encryptedKey; }
-		}
+        public Asn1OctetString EncryptedKey => m_encryptedKey;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -101,10 +79,11 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(version);
-            v.AddOptionalTagged(false, 0, keyDerivationAlgorithm);
-			v.Add(keyEncryptionAlgorithm, encryptedKey);
-			return new DerSequence(v);
+            if (m_keyDerivationAlgorithm == null)
+                return new DerSequence(m_version, m_keyEncryptionAlgorithm, m_encryptedKey);
+
+            return new DerSequence(m_version, new DerTaggedObject(false, 0, m_keyDerivationAlgorithm),
+                m_keyEncryptionAlgorithm, m_encryptedKey);
         }
     }
 }
diff --git a/crypto/src/asn1/cms/RecipientEncryptedKey.cs b/crypto/src/asn1/cms/RecipientEncryptedKey.cs
index 0e0214909..3a31b3c93 100644
--- a/crypto/src/asn1/cms/RecipientEncryptedKey.cs
+++ b/crypto/src/asn1/cms/RecipientEncryptedKey.cs
@@ -1,3 +1,5 @@
+using System;
+
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class RecipientEncryptedKey
@@ -17,33 +19,28 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new RecipientEncryptedKey(Asn1Sequence.GetInstance(obj, isExplicit));
         }
 
-        private readonly KeyAgreeRecipientIdentifier identifier;
-		private readonly Asn1OctetString encryptedKey;
+        private readonly KeyAgreeRecipientIdentifier m_identifier;
+		private readonly Asn1OctetString m_encryptedKey;
 
-		private RecipientEncryptedKey(
-			Asn1Sequence seq)
+		private RecipientEncryptedKey(Asn1Sequence seq)
 		{
-			identifier = KeyAgreeRecipientIdentifier.GetInstance(seq[0]);
-			encryptedKey = (Asn1OctetString) seq[1];
-		}
+            int count = seq.Count;
+            if (count != 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-        public RecipientEncryptedKey(
-			KeyAgreeRecipientIdentifier	id,
-			Asn1OctetString				encryptedKey)
-		{
-			this.identifier = id;
-			this.encryptedKey = encryptedKey;
+            m_identifier = KeyAgreeRecipientIdentifier.GetInstance(seq[0]);
+			m_encryptedKey = Asn1OctetString.GetInstance(seq[1]);
 		}
 
-		public KeyAgreeRecipientIdentifier Identifier
-		{
-			get { return identifier; }
-		}
+        public RecipientEncryptedKey(KeyAgreeRecipientIdentifier id, Asn1OctetString encryptedKey)
+        {
+            m_identifier = id ?? throw new ArgumentNullException(nameof(id));
+            m_encryptedKey = encryptedKey ?? throw new ArgumentNullException(nameof(encryptedKey));
+        }
 
-		public Asn1OctetString EncryptedKey
-		{
-			get { return encryptedKey; }
-		}
+        public KeyAgreeRecipientIdentifier Identifier => m_identifier;
+
+		public Asn1OctetString EncryptedKey => m_encryptedKey;
 
 		/** 
 		 * Produce an object suitable for an Asn1OutputStream.
@@ -54,9 +51,6 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 * }
 		 * </pre>
 		 */
-		public override Asn1Object ToAsn1Object()
-		{
-			return new DerSequence(identifier, encryptedKey);
-		}
+		public override Asn1Object ToAsn1Object() => new DerSequence(m_identifier, m_encryptedKey);
 	}
 }
diff --git a/crypto/src/asn1/cms/RecipientIdentifier.cs b/crypto/src/asn1/cms/RecipientIdentifier.cs
index de9fd5e75..4fe181dff 100644
--- a/crypto/src/asn1/cms/RecipientIdentifier.cs
+++ b/crypto/src/asn1/cms/RecipientIdentifier.cs
@@ -32,7 +32,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
 		public RecipientIdentifier(IssuerAndSerialNumber id)
         {
-            m_id = id;
+            m_id = id ?? throw new ArgumentNullException(nameof(id));
         }
 
 		public RecipientIdentifier(Asn1OctetString id)
@@ -42,7 +42,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
 		public RecipientIdentifier(Asn1Object id)
         {
-            m_id = id;
+            m_id = id ?? throw new ArgumentNullException(nameof(id));
         }
 
         public bool IsTagged => m_id is Asn1TaggedObject;
diff --git a/crypto/src/asn1/cms/RecipientInfo.cs b/crypto/src/asn1/cms/RecipientInfo.cs
index 85bd7e585..424ea4d10 100644
--- a/crypto/src/asn1/cms/RecipientInfo.cs
+++ b/crypto/src/asn1/cms/RecipientInfo.cs
@@ -29,107 +29,100 @@ namespace Org.BouncyCastle.Asn1.Cms
             return Asn1Utilities.GetInstanceFromChoice(taggedObject, declaredExplicit, GetInstance);
         }
 
-        internal Asn1Encodable info;
+        private readonly Asn1Encodable m_info;
 
-		public RecipientInfo(
-            KeyTransRecipientInfo info)
+		public RecipientInfo(KeyTransRecipientInfo info)
         {
-            this.info = info;
+            m_info = info ?? throw new ArgumentNullException(nameof(info));
         }
 
-		public RecipientInfo(
-            KeyAgreeRecipientInfo info)
+		public RecipientInfo(KeyAgreeRecipientInfo info)
         {
-            this.info = new DerTaggedObject(false, 1, info);
+            m_info = new DerTaggedObject(false, 1, info);
         }
 
-		public RecipientInfo(
-            KekRecipientInfo info)
+		public RecipientInfo(KekRecipientInfo info)
         {
-            this.info = new DerTaggedObject(false, 2, info);
+            m_info = new DerTaggedObject(false, 2, info);
         }
 
-		public RecipientInfo(
-            PasswordRecipientInfo info)
+		public RecipientInfo(PasswordRecipientInfo info)
         {
-            this.info = new DerTaggedObject(false, 3, info);
+            m_info = new DerTaggedObject(false, 3, info);
         }
 
-		public RecipientInfo(
-            OtherRecipientInfo info)
+		public RecipientInfo(OtherRecipientInfo info)
         {
-            this.info = new DerTaggedObject(false, 4, info);
+            m_info = new DerTaggedObject(false, 4, info);
         }
 
-		public RecipientInfo(
-            Asn1Object   info)
+		public RecipientInfo(Asn1Object info)
         {
-            this.info = info;
+            m_info = info ?? throw new ArgumentNullException(nameof(info));
         }
 
+        [Obsolete("Will be removed")]
 		public DerInteger Version
         {
 			get
 			{
-				if (info is Asn1TaggedObject o)
-				{
-					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 DerInteger.Zero;    // no syntax version for OtherRecipientInfo
-					default:
-						throw new InvalidOperationException("unknown tag");
-					}
-				}
-
-				return KeyTransRecipientInfo.GetInstance(info).Version;
+				if (!(m_info is Asn1TaggedObject tagged))
+                    return KeyTransRecipientInfo.GetInstance(m_info).Version;
+
+                if (tagged.HasContextTag())
+                {
+                    switch (tagged.TagNo)
+                    {
+                    case 1:
+                        return KeyAgreeRecipientInfo.GetInstance(tagged, false).Version;
+                    case 2:
+                        return GetKekInfo(tagged).Version;
+                    case 3:
+                        return PasswordRecipientInfo.GetInstance(tagged, false).Version;
+                    case 4:
+                        return DerInteger.Zero;    // no syntax version for OtherRecipientInfo
+                    }
+                }
+                throw new InvalidOperationException("unknown tag");
 			}
         }
 
-		public bool IsTagged
-		{
-			get { return info is Asn1TaggedObject; }
-		}
+		public bool IsTagged => m_info is Asn1TaggedObject;
 
 		public Asn1Encodable Info
         {
 			get
 			{
-				if (info is Asn1TaggedObject o)
-				{
-					switch (o.TagNo)
+				if (!(m_info is Asn1TaggedObject tagged))
+                    return KeyTransRecipientInfo.GetInstance(m_info);
+
+                if (tagged.HasContextTag())
+                {
+                    switch (tagged.TagNo)
 					{
 					case 1:
-						return KeyAgreeRecipientInfo.GetInstance(o, false);
+						return KeyAgreeRecipientInfo.GetInstance(tagged, false);
 					case 2:
-						return GetKekInfo(o);
+						return GetKekInfo(tagged);
 					case 3:
-						return PasswordRecipientInfo.GetInstance(o, false);
+						return PasswordRecipientInfo.GetInstance(tagged, false);
 					case 4:
-						return OtherRecipientInfo.GetInstance(o, false);
-					default:
-						throw new InvalidOperationException("unknown tag");
+						return OtherRecipientInfo.GetInstance(tagged, false);
 					}
-				}
-
-				return KeyTransRecipientInfo.GetInstance(info);
+                }
+                throw new InvalidOperationException("unknown tag");
 			}
         }
 
-		private KekRecipientInfo GetKekInfo(
-			Asn1TaggedObject o)
-		{
-			// For compatibility with erroneous version, we don't always pass 'false' here
-			return KekRecipientInfo.GetInstance(o, o.IsExplicit());
-		}
+        private KekRecipientInfo GetKekInfo(Asn1TaggedObject tagged)
+        {
+            // For compatibility with erroneous version, we don't always pass 'false' here
+            bool declaredExplicit = tagged.IsExplicit();
+
+            return KekRecipientInfo.GetInstance(tagged, declaredExplicit);
+        }
 
-		/**
+        /**
          * Produce an object suitable for an Asn1OutputStream.
          * <pre>
          * RecipientInfo ::= CHOICE {
@@ -140,9 +133,30 @@ namespace Org.BouncyCastle.Asn1.Cms
          *     ori [4] OtherRecipientInfo }
          * </pre>
          */
-        public override Asn1Object ToAsn1Object()
+        public override Asn1Object ToAsn1Object() => m_info.ToAsn1Object();
+
+        internal bool IsKeyTransV0()
+        {
+            if (m_info is Asn1TaggedObject)
+                return false;
+
+            var ktri = KeyTransRecipientInfo.GetInstance(m_info);
+
+            return ktri.Version.HasValue(0);
+        }
+
+        internal bool IsPasswordOrOther()
         {
-            return info.ToAsn1Object();
+            if (m_info is Asn1TaggedObject tagged && tagged.HasContextTag())
+            {
+                switch (tagged.TagNo)
+                {
+                case 3:
+                case 4:
+                    return true;
+                }
+            }
+            return false;
         }
     }
 }
diff --git a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
index daae426d2..8bb82f924 100644
--- a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
+++ b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
@@ -25,78 +25,50 @@ namespace Org.BouncyCastle.Asn1.Cms
 #pragma warning restore CS0618 // Type or member is obsolete
         }
 
-        private Asn1OctetString      subjectKeyIdentifier;
-        private Asn1GeneralizedTime  date;
-        private OtherKeyAttribute    other;
+        private readonly Asn1OctetString m_subjectKeyIdentifier;
+        private readonly Asn1GeneralizedTime m_date;
+        private readonly OtherKeyAttribute m_other;
 
-		public RecipientKeyIdentifier(
-            Asn1OctetString         subjectKeyIdentifier,
-            Asn1GeneralizedTime     date,
-            OtherKeyAttribute       other)
+        public RecipientKeyIdentifier(Asn1OctetString subjectKeyIdentifier, Asn1GeneralizedTime date,
+            OtherKeyAttribute other)
         {
-            this.subjectKeyIdentifier = subjectKeyIdentifier;
-            this.date = date;
-            this.other = other;
+            m_subjectKeyIdentifier = subjectKeyIdentifier ?? throw new ArgumentNullException(nameof(subjectKeyIdentifier));
+            m_date = date;
+            m_other = other;
         }
-		
-		public RecipientKeyIdentifier(
-			byte[] subjectKeyIdentifier)
+
+        public RecipientKeyIdentifier(byte[] subjectKeyIdentifier)
 			: this(subjectKeyIdentifier, null, null)
 		{
 		}
 
-		public RecipientKeyIdentifier(
-			byte[]				subjectKeyIdentifier,
-            Asn1GeneralizedTime date,
-			OtherKeyAttribute	other)
+		public RecipientKeyIdentifier(byte[] subjectKeyIdentifier, Asn1GeneralizedTime date, OtherKeyAttribute other)
 		{
-			this.subjectKeyIdentifier = new DerOctetString(subjectKeyIdentifier);
-			this.date = date;
-			this.other = other;
+			m_subjectKeyIdentifier = new DerOctetString(subjectKeyIdentifier);
+			m_date = date;
+			m_other = other;
 		}
 
         [Obsolete("Use 'GetInstance' instead")]
         public RecipientKeyIdentifier(Asn1Sequence seq)
         {
-            subjectKeyIdentifier = Asn1OctetString.GetInstance(seq[0]);
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 3)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_subjectKeyIdentifier = Asn1OctetString.GetInstance(seq[pos++]);
+            m_date = Asn1Utilities.ReadOptional(seq, ref pos, Asn1GeneralizedTime.GetOptional);
+            m_other = Asn1Utilities.ReadOptional(seq, ref pos, OtherKeyAttribute.GetOptional);
 
-			switch(seq.Count)
-            {
-			case 1:
-				break;
-			case 2:
-				if (seq[1] is Asn1GeneralizedTime asn1GeneralizedTime)
-				{
-					date = asn1GeneralizedTime;
-				}
-				else
-				{
-					other = OtherKeyAttribute.GetInstance(seq[2]);
-				}
-				break;
-			case 3:
-				date = (Asn1GeneralizedTime)seq[1];
-				other = OtherKeyAttribute.GetInstance(seq[2]);
-				break;
-			default:
-				throw new ArgumentException("Invalid RecipientKeyIdentifier");
-            }
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public Asn1OctetString SubjectKeyIdentifier
-		{
-			get { return subjectKeyIdentifier; }
-		}
+        public Asn1OctetString SubjectKeyIdentifier => m_subjectKeyIdentifier;
 
-		public Asn1GeneralizedTime Date
-		{
-			get { return date; }
-		}
+		public Asn1GeneralizedTime Date => m_date;
 
-		public OtherKeyAttribute OtherKeyAttribute
-		{
-			get { return other; }
-		}
+        public OtherKeyAttribute OtherKeyAttribute => m_other;
 
 		/**
          * Produce an object suitable for an Asn1OutputStream.
@@ -112,8 +84,9 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(subjectKeyIdentifier);
-			v.AddOptional(date, other);
+            Asn1EncodableVector v = new Asn1EncodableVector(3);
+            v.Add(m_subjectKeyIdentifier);
+			v.AddOptional(m_date, m_other);
 			return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cms/SCVPReqRes.cs b/crypto/src/asn1/cms/SCVPReqRes.cs
index ec36ff9fa..11df63ea9 100644
--- a/crypto/src/asn1/cms/SCVPReqRes.cs
+++ b/crypto/src/asn1/cms/SCVPReqRes.cs
@@ -1,4 +1,6 @@
-namespace Org.BouncyCastle.Asn1.Cms
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cms
 {
     public class ScvpReqRes
         : Asn1Encodable
@@ -17,21 +19,20 @@
             return new ScvpReqRes(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private readonly ContentInfo request;
-        private readonly ContentInfo response;
+        private readonly ContentInfo m_request;
+        private readonly ContentInfo m_response;
 
         private ScvpReqRes(Asn1Sequence seq)
         {
-            if (seq[0] is Asn1TaggedObject taggedObject)
-            {
-                this.request = ContentInfo.GetInstance(taggedObject, true);
-                this.response = ContentInfo.GetInstance(seq[1]);
-            }
-            else
-            {
-                this.request = null;
-                this.response = ContentInfo.GetInstance(seq[0]);
-            }
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_request = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, true, ContentInfo.GetInstance);
+            m_response = ContentInfo.GetInstance(seq[pos++]);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
         public ScvpReqRes(ContentInfo response)
@@ -41,19 +42,13 @@
 
         public ScvpReqRes(ContentInfo request, ContentInfo response)
         {
-            this.request = request;
-            this.response = response;
+            m_request = request;
+            m_response = response ?? throw new ArgumentNullException(nameof(response));
         }
 
-        public virtual ContentInfo Request
-        {
-            get { return request; }
-        }
+        public virtual ContentInfo Request => m_request;
 
-        public virtual ContentInfo Response
-        {
-            get { return response; }
-        }
+        public virtual ContentInfo Response => m_response;
 
         /**
          * <pre>
@@ -65,10 +60,10 @@
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(2);
-            v.AddOptionalTagged(true, 0, request);
-            v.Add(response);
-            return new DerSequence(v);
+            if (m_request == null)
+                return new DerSequence(m_response);
+
+            return new DerSequence(new DerTaggedObject(true, 0, m_request), m_response);
         }
     }
 }
diff --git a/crypto/src/asn1/cms/SignedData.cs b/crypto/src/asn1/cms/SignedData.cs
index 380305c32..4ceb0155b 100644
--- a/crypto/src/asn1/cms/SignedData.cs
+++ b/crypto/src/asn1/cms/SignedData.cs
@@ -22,213 +22,56 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new SignedData(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private static readonly DerInteger Version1 = DerInteger.One;
-        private static readonly DerInteger Version3 = DerInteger.Three;
-        private static readonly DerInteger Version4 = DerInteger.Four;
-        private static readonly DerInteger Version5 = DerInteger.Five;
-
-        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 SignedData(
-            Asn1Set     digestAlgorithms,
-            ContentInfo contentInfo,
-            Asn1Set     certificates,
-            Asn1Set     crls,
-            Asn1Set     signerInfos)
+        private readonly DerInteger m_version;
+        private readonly Asn1Set m_digestAlgorithms;
+        private readonly ContentInfo m_contentInfo;
+        private readonly Asn1Set m_certificates;
+        private readonly Asn1Set m_crls;
+        private readonly Asn1Set m_signerInfos;
+        private readonly bool m_certsBer;
+        private readonly bool m_crlsBer;
+
+        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 tagged)
-                    {
-                        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.HasValue(3))
-                {
-                    return true;
-                }
-            }
-
-            return false;
+            m_digestAlgorithms = digestAlgorithms ?? throw new ArgumentNullException(nameof(digestAlgorithms));
+            m_contentInfo = contentInfo ?? throw new ArgumentNullException(nameof(contentInfo));
+            m_certificates = certificates;
+            m_crls = crls;
+            m_signerInfos = signerInfos ?? throw new ArgumentNullException(nameof(signerInfos));
+            m_crlsBer = crls is BerSet;
+            m_certsBer = certificates is BerSet;
+            m_version = CalculateVersionField(contentInfo.ContentType, certificates, crls, signerInfos);
         }
 
         private SignedData(Asn1Sequence seq)
         {
-            var e = seq.GetEnumerator();
-
-            e.MoveNext();
-            version = (DerInteger)e.Current;
-
-            e.MoveNext();
-            digestAlgorithms = (Asn1Set)e.Current.ToAsn1Object();
-
-            e.MoveNext();
-            contentInfo = ContentInfo.GetInstance(e.Current.ToAsn1Object());
-
-            while (e.MoveNext())
-            {
-                Asn1Object o = e.Current.ToAsn1Object();
-
-                //
-                // 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 tagged)
-                {
-                    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;
-                }
-            }
+            int count = seq.Count, pos = 0;
+            if (count < 4 || count > 6)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_digestAlgorithms = Asn1Set.GetInstance(seq[pos++]);
+            m_contentInfo = ContentInfo.GetInstance(seq[pos++]);
+            m_certificates = ReadOptionalTaggedSet(seq, ref pos, 0, out m_certsBer);
+            m_crls = ReadOptionalTaggedSet(seq, ref pos, 1, out m_crlsBer);
+            m_signerInfos = Asn1Set.GetInstance(seq[pos++]);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public DerInteger Version
-        {
-            get { return version; }
-        }
+        public DerInteger Version => m_version;
 
-        public Asn1Set DigestAlgorithms
-        {
-            get { return digestAlgorithms; }
-        }
+        public Asn1Set DigestAlgorithms => m_digestAlgorithms;
 
-        public ContentInfo EncapContentInfo
-        {
-            get { return contentInfo; }
-        }
+        public ContentInfo EncapContentInfo => m_contentInfo;
 
-        public Asn1Set Certificates
-        {
-            get { return certificates; }
-        }
+        public Asn1Set Certificates => m_certificates;
 
-        public Asn1Set CRLs
-        {
-            get { return crls; }
-        }
+        public Asn1Set CRLs => m_crls;
 
-        public Asn1Set SignerInfos
-        {
-            get { return signerInfos; }
-        }
+        public Asn1Set SignerInfos => m_signerInfos;
 
         /**
          * Produce an object suitable for an Asn1OutputStream.
@@ -245,36 +88,135 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(
-                version, digestAlgorithms, contentInfo);
+            Asn1EncodableVector v = new Asn1EncodableVector(6);
+            v.Add(m_version, m_digestAlgorithms, m_contentInfo);
 
-            if (certificates != null)
+            if (m_certificates != null)
             {
-                if (certsBer)
+                if (m_certsBer)
                 {
-                    v.Add(new BerTaggedObject(false, 0, certificates));
+                    v.Add(new BerTaggedObject(false, 0, m_certificates));
                 }
                 else
                 {
-                    v.Add(new DerTaggedObject(false, 0, certificates));
+                    v.Add(new DerTaggedObject(false, 0, m_certificates));
                 }
             }
 
-            if (crls != null)
+            if (m_crls != null)
             {
-                if (crlsBer)
+                if (m_crlsBer)
                 {
-                    v.Add(new BerTaggedObject(false, 1, crls));
+                    v.Add(new BerTaggedObject(false, 1, m_crls));
                 }
                 else
                 {
-                    v.Add(new DerTaggedObject(false, 1, crls));
+                    v.Add(new DerTaggedObject(false, 1, m_crls));
                 }
             }
 
-            v.Add(signerInfos);
+            v.Add(m_signerInfos);
 
             return new BerSequence(v);
         }
+
+        private static DerInteger CalculateVersionField(DerObjectIdentifier contentOid, Asn1Set certs, Asn1Set crls,
+            Asn1Set signerInfs)
+        {
+            /*
+             * 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
+             */
+
+            if (crls != null)
+            {
+                foreach (var element in crls)
+                {
+                    var tagged = Asn1TaggedObject.GetOptional(element);
+                    if (tagged != null)
+                    {
+                        // RevocationInfoChoice.other
+                        if (tagged.HasContextTag(1))
+                            return DerInteger.Five;
+                    }
+                }
+            }
+
+            bool anyV1AttrCerts = false;
+
+            if (certs != null)
+            {
+                bool anyV2AttrCerts = false;
+
+                foreach (var element in certs)
+                {
+                    var tagged = Asn1TaggedObject.GetOptional(element);
+                    if (tagged != null)
+                    {
+                        // CertificateChoices.other
+                        if (tagged.HasContextTag(3))
+                            return DerInteger.Five;
+
+                        // CertificateChoices.v2AttrCert
+                        anyV2AttrCerts = anyV2AttrCerts || tagged.HasContextTag(2);
+
+                        // CertificateChoices.v1AttrCert
+                        anyV1AttrCerts = anyV1AttrCerts || tagged.HasContextTag(1);
+                    }
+                }
+
+                if (anyV2AttrCerts)
+                    return DerInteger.Four;
+            }
+
+            if (anyV1AttrCerts || !CmsObjectIdentifiers.Data.Equals(contentOid) || HasV3SignerInfos(signerInfs))
+                return DerInteger.Three;
+
+            return DerInteger.One;
+        }
+
+        // (any SignerInfo structures are version 3)
+        private static bool HasV3SignerInfos(Asn1Set signerInfs)
+        {
+            foreach (object obj in signerInfs)
+            {
+                var signerInfo = SignerInfo.GetInstance(obj);
+                if (signerInfo.Version.HasValue(3))
+                    return true;
+            }
+            return false;
+        }
+
+        private static Asn1Set ReadOptionalTaggedSet(Asn1Sequence sequence, ref int sequencePosition, int tagNo,
+            out bool isBer)
+        {
+            if (sequencePosition < sequence.Count &&
+                sequence[sequencePosition] is Asn1TaggedObject taggedObject &&
+                taggedObject.HasContextTag(tagNo))
+            {
+                var result = Asn1Set.GetInstance(taggedObject, false);
+                sequencePosition++;
+                isBer = taggedObject is BerTaggedObject;
+                return result;
+            }
+
+            isBer = default;
+            return null;
+        }
     }
 }
diff --git a/crypto/src/asn1/cms/SignerIdentifier.cs b/crypto/src/asn1/cms/SignerIdentifier.cs
index 17f3f08c2..055a393df 100644
--- a/crypto/src/asn1/cms/SignerIdentifier.cs
+++ b/crypto/src/asn1/cms/SignerIdentifier.cs
@@ -32,39 +32,33 @@ namespace Org.BouncyCastle.Asn1.Cms
             return Asn1Utilities.GetInstanceFromChoice(taggedObject, declaredExplicit, GetInstance);
         }
 
-        private Asn1Encodable id;
+        private readonly Asn1Encodable m_id;
 
-		public SignerIdentifier(
-            IssuerAndSerialNumber id)
+        public SignerIdentifier(IssuerAndSerialNumber id)
         {
-            this.id = id;
+            m_id = id ?? throw new ArgumentNullException(nameof(id));
         }
 
-		public SignerIdentifier(
-            Asn1OctetString id)
+		public SignerIdentifier(Asn1OctetString id)
         {
-            this.id = new DerTaggedObject(false, 0, id);
+            m_id = new DerTaggedObject(false, 0, id);
         }
 
-		public SignerIdentifier(
-            Asn1Object id)
+		public SignerIdentifier(Asn1Object id)
         {
-            this.id = id;
+            m_id = id ?? throw new ArgumentNullException(nameof(id));
         }
 
-		public bool IsTagged
-		{
-			get { return (id is Asn1TaggedObject); }
-		}
+        public bool IsTagged => m_id is Asn1TaggedObject;
 
         public Asn1Encodable ID
         {
             get
             {
-                if (id is Asn1TaggedObject taggedObject)
+                if (m_id is Asn1TaggedObject taggedObject)
                     return Asn1OctetString.GetInstance(taggedObject, false);
 
-                return id;
+                return m_id;
             }
         }
 
@@ -79,9 +73,6 @@ namespace Org.BouncyCastle.Asn1.Cms
          * SubjectKeyIdentifier ::= OCTET STRING
          * </pre>
          */
-        public override Asn1Object ToAsn1Object()
-        {
-            return id.ToAsn1Object();
-        }
+        public override Asn1Object ToAsn1Object() => m_id.ToAsn1Object();
     }
 }
diff --git a/crypto/src/asn1/cms/SignerInfo.cs b/crypto/src/asn1/cms/SignerInfo.cs
index bce33e699..fbd4bcca7 100644
--- a/crypto/src/asn1/cms/SignerInfo.cs
+++ b/crypto/src/asn1/cms/SignerInfo.cs
@@ -1,3 +1,5 @@
+using System;
+
 using Org.BouncyCastle.Asn1.X509;
 
 namespace Org.BouncyCastle.Asn1.Cms
@@ -19,124 +21,66 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new SignerInfo(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private DerInteger              version;
-        private SignerIdentifier        sid;
-        private AlgorithmIdentifier     digAlgorithm;
-        private Asn1Set                 authenticatedAttributes;
-        private AlgorithmIdentifier     digEncryptionAlgorithm;
-        private Asn1OctetString         encryptedDigest;
-        private Asn1Set                 unauthenticatedAttributes;
-
-        public SignerInfo(
-            SignerIdentifier        sid,
-            AlgorithmIdentifier     digAlgorithm,
-            Asn1Set                 authenticatedAttributes,
-            AlgorithmIdentifier     digEncryptionAlgorithm,
-            Asn1OctetString         encryptedDigest,
-            Asn1Set                 unauthenticatedAttributes)
+        private readonly DerInteger m_version;
+        private readonly SignerIdentifier m_sid;
+        private readonly AlgorithmIdentifier m_digAlgorithm;
+        private readonly Asn1Set m_authenticatedAttributes;
+        private readonly AlgorithmIdentifier m_digEncryptionAlgorithm;
+        private readonly Asn1OctetString m_encryptedDigest;
+        private readonly Asn1Set m_unauthenticatedAttributes;
+
+        public SignerInfo(SignerIdentifier sid, AlgorithmIdentifier digAlgorithm, Attributes authenticatedAttributes,
+            AlgorithmIdentifier digEncryptionAlgorithm, Asn1OctetString encryptedDigest,
+            Attributes unauthenticatedAttributes)
+            : this(sid, digAlgorithm, Asn1Set.GetInstance(authenticatedAttributes), digEncryptionAlgorithm,
+                  encryptedDigest, Asn1Set.GetInstance(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)
+        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 = Asn1Set.GetInstance(authenticatedAttributes);
-            this.digEncryptionAlgorithm = digEncryptionAlgorithm;
-            this.encryptedDigest = encryptedDigest;
-            this.unauthenticatedAttributes = Asn1Set.GetInstance(unauthenticatedAttributes);
+            m_sid = sid ?? throw new ArgumentNullException(nameof(sid));
+            m_digAlgorithm = digAlgorithm ?? throw new ArgumentNullException(nameof(digAlgorithm));
+            m_authenticatedAttributes = authenticatedAttributes;
+            m_digEncryptionAlgorithm = digEncryptionAlgorithm ?? throw new ArgumentNullException(nameof(digEncryptionAlgorithm));
+            m_encryptedDigest = encryptedDigest ?? throw new ArgumentNullException(nameof(encryptedDigest));
+            m_unauthenticatedAttributes = unauthenticatedAttributes;
+            m_version = sid.IsTagged ? DerInteger.Three : DerInteger.One;
         }
 
         private SignerInfo(Asn1Sequence seq)
         {
-            var e = seq.GetEnumerator();
-
-            e.MoveNext();
-            version = (DerInteger)e.Current;
-
-            e.MoveNext();
-            sid = SignerIdentifier.GetInstance(e.Current.ToAsn1Object());
-
-            e.MoveNext();
-            digAlgorithm = AlgorithmIdentifier.GetInstance(e.Current.ToAsn1Object());
-
-            e.MoveNext();
-            var obj = e.Current.ToAsn1Object();
-
-            if (obj is Asn1TaggedObject tagged)
-            {
-                authenticatedAttributes = Asn1Set.GetInstance(tagged, false);
-
-                e.MoveNext();
-                digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(e.Current.ToAsn1Object());
-            }
-            else
-            {
-                authenticatedAttributes = null;
-                digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(obj);
-            }
-
-            e.MoveNext();
-            encryptedDigest = Asn1OctetString.GetInstance(e.Current.ToAsn1Object());
-
-            if (e.MoveNext())
-            {
-                unauthenticatedAttributes = Asn1Set.GetInstance((Asn1TaggedObject)e.Current.ToAsn1Object(), false);
-            }
-            else
-            {
-                unauthenticatedAttributes = null;
-            }
+            int count = seq.Count, pos = 0;
+            if (count < 5 || count > 7)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
+
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_sid = SignerIdentifier.GetInstance(seq[pos++]);
+            m_digAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_authenticatedAttributes = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 0, false, Asn1Set.GetInstance);
+            m_digEncryptionAlgorithm = AlgorithmIdentifier.GetInstance(seq[pos++]);
+            m_encryptedDigest = Asn1OctetString.GetInstance(seq[pos++]);
+            m_unauthenticatedAttributes = Asn1Utilities.ReadOptionalContextTagged(seq, ref pos, 1, false, Asn1Set.GetInstance);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
         }
 
-        public DerInteger Version
-        {
-            get { return version; }
-        }
+        public DerInteger Version => m_version;
 
-        public SignerIdentifier SignerID
-        {
-            get { return sid; }
-        }
+        public SignerIdentifier SignerID => m_sid;
 
-        public Asn1Set AuthenticatedAttributes
-        {
-            get { return authenticatedAttributes; }
-        }
+        public Asn1Set AuthenticatedAttributes => m_authenticatedAttributes;
 
-        public AlgorithmIdentifier DigestAlgorithm
-        {
-            get { return digAlgorithm; }
-        }
+        public AlgorithmIdentifier DigestAlgorithm => m_digAlgorithm;
 
-        public Asn1OctetString EncryptedDigest
-        {
-            get { return encryptedDigest; }
-        }
+        public Asn1OctetString EncryptedDigest => m_encryptedDigest;
 
-        public AlgorithmIdentifier DigestEncryptionAlgorithm
-        {
-            get { return digEncryptionAlgorithm; }
-        }
+        public AlgorithmIdentifier DigestEncryptionAlgorithm => m_digEncryptionAlgorithm;
 
-        public Asn1Set UnauthenticatedAttributes
-        {
-            get { return unauthenticatedAttributes; }
-        }
+        public Asn1Set UnauthenticatedAttributes => m_unauthenticatedAttributes;
 
         /**
          * Produce an object suitable for an Asn1OutputStream.
@@ -160,10 +104,11 @@ namespace Org.BouncyCastle.Asn1.Cms
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(version, sid, digAlgorithm);
-            v.AddOptionalTagged(false, 0, authenticatedAttributes);
-            v.Add(digEncryptionAlgorithm, encryptedDigest);
-            v.AddOptionalTagged(false, 1, unauthenticatedAttributes);
+            Asn1EncodableVector v = new Asn1EncodableVector(7);
+            v.Add(m_version, m_sid, m_digAlgorithm);
+            v.AddOptionalTagged(false, 0, m_authenticatedAttributes);
+            v.Add(m_digEncryptionAlgorithm, m_encryptedDigest);
+            v.AddOptionalTagged(false, 1, m_unauthenticatedAttributes);
             return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/cms/TimeStampAndCRL.cs b/crypto/src/asn1/cms/TimeStampAndCRL.cs
index c0cd48905..a76ce7680 100644
--- a/crypto/src/asn1/cms/TimeStampAndCRL.cs
+++ b/crypto/src/asn1/cms/TimeStampAndCRL.cs
@@ -1,3 +1,7 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class TimeStampAndCrl
@@ -17,33 +21,37 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new TimeStampAndCrl(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private ContentInfo timeStamp;
-		private X509.CertificateList crl;
+        private readonly ContentInfo m_timeStamp;
+		private readonly CertificateList m_crl;
 
 		public TimeStampAndCrl(ContentInfo timeStamp)
+			: this(timeStamp, null)
 		{
-			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 TimeStampAndCrl(ContentInfo timeStamp, CertificateList crl)
+        {
+            m_timeStamp = timeStamp ?? throw new ArgumentNullException(nameof(timeStamp));
+            m_crl = crl;
+        }
 
-		public virtual ContentInfo TimeStampToken
+        private TimeStampAndCrl(Asn1Sequence seq)
 		{
-			get { return this.timeStamp; }
-		}
+            int count = seq.Count, pos = 0;
+            if (count < 1 || count > 2)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-		public virtual X509.CertificateList Crl
-		{
-			get { return this.crl; }
+            m_timeStamp = ContentInfo.GetInstance(seq[pos++]);
+            m_crl = Asn1Utilities.ReadOptional(seq, ref pos, CertificateList.GetOptional);
+
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
 		}
 
+		public virtual ContentInfo TimeStampToken => m_timeStamp;
+
+		public virtual CertificateList Crl => m_crl;
+
 		/**
 		 * <pre>
 		 * TimeStampAndCRL ::= SEQUENCE {
@@ -55,9 +63,9 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(timeStamp);
-			v.AddOptional(crl);
-			return new DerSequence(v);
+			return m_crl == null
+				?  new DerSequence(m_timeStamp)
+                :  new DerSequence(m_timeStamp, m_crl);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cms/TimeStampTokenEvidence.cs b/crypto/src/asn1/cms/TimeStampTokenEvidence.cs
index 6ac1eb11d..f7c5298a7 100644
--- a/crypto/src/asn1/cms/TimeStampTokenEvidence.cs
+++ b/crypto/src/asn1/cms/TimeStampTokenEvidence.cs
@@ -1,3 +1,7 @@
+using System;
+
+using Org.BouncyCastle.Utilities;
+
 namespace Org.BouncyCastle.Asn1.Cms
 {
     public class TimeStampTokenEvidence
@@ -17,34 +21,27 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new TimeStampTokenEvidence(Asn1Sequence.GetInstance(tagged, isExplicit));
         }
 
-        private TimeStampAndCrl[] timeStampAndCrls;
+        private readonly TimeStampAndCrl[] m_timeStampAndCrls;
 
 		public TimeStampTokenEvidence(TimeStampAndCrl[] timeStampAndCrls)
 		{
-			this.timeStampAndCrls = timeStampAndCrls;
+			if (Arrays.IsNullOrContainsNull(timeStampAndCrls))
+                throw new NullReferenceException("'timeStampAndCrls' cannot be null, or contain null");
+
+            m_timeStampAndCrls = timeStampAndCrls;
 		}
 
 		public TimeStampTokenEvidence(TimeStampAndCrl timeStampAndCrl)
 		{
-			this.timeStampAndCrls = new TimeStampAndCrl[]{ timeStampAndCrl };
+			m_timeStampAndCrls = new []{ timeStampAndCrl ?? throw new ArgumentNullException(nameof(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());
-			}
+			m_timeStampAndCrls = seq.MapElements(TimeStampAndCrl.GetInstance);
 		}
 
-        public virtual TimeStampAndCrl[] ToTimeStampAndCrlArray()
-		{
-			return (TimeStampAndCrl[])timeStampAndCrls.Clone();
-		}
+        public virtual TimeStampAndCrl[] ToTimeStampAndCrlArray() => (TimeStampAndCrl[])m_timeStampAndCrls.Clone();
 
 		/**
 		 * <pre>
@@ -53,9 +50,6 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 * </pre>
 		 * @return
 		 */
-		public override Asn1Object ToAsn1Object()
-		{
-			return new DerSequence(timeStampAndCrls);
-		}
+		public override Asn1Object ToAsn1Object() => new DerSequence(m_timeStampAndCrls);
 	}
 }
diff --git a/crypto/src/asn1/cms/TimeStampedData.cs b/crypto/src/asn1/cms/TimeStampedData.cs
index cae290609..53a8334ec 100644
--- a/crypto/src/asn1/cms/TimeStampedData.cs
+++ b/crypto/src/asn1/cms/TimeStampedData.cs
@@ -19,63 +19,45 @@ namespace Org.BouncyCastle.Asn1.Cms
             return new TimeStampedData(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
         }
 
-        private DerInteger version;
-		private DerIA5String dataUri;
-		private MetaData metaData;
-		private Asn1OctetString content;
-		private Evidence temporalEvidence;
+        private readonly DerInteger m_version;
+        private readonly DerIA5String m_dataUri;
+        private readonly MetaData m_metaData;
+        private readonly Asn1OctetString m_content;
+        private readonly Evidence m_temporalEvidence;
 
-		public TimeStampedData(DerIA5String dataUri, MetaData metaData, Asn1OctetString content,
-			Evidence temporalEvidence)
-		{
-			this.version = DerInteger.One;
-			this.dataUri = dataUri;
-			this.metaData = metaData;
-			this.content = content;
-			this.temporalEvidence = temporalEvidence;
-		}
+        public TimeStampedData(DerIA5String dataUri, MetaData metaData, Asn1OctetString content,
+            Evidence temporalEvidence)
+        {
+            m_version = DerInteger.One;
+            m_dataUri = dataUri;
+            m_metaData = metaData;
+            m_content = content;
+            m_temporalEvidence = temporalEvidence ?? throw new ArgumentNullException(nameof(temporalEvidence));
+        }
 
-		private TimeStampedData(Asn1Sequence seq)
+        private TimeStampedData(Asn1Sequence seq)
 		{
-			this.version = DerInteger.GetInstance(seq[0]);
-			
-			int index = 1;
-			if (seq[index] is DerIA5String ia5)
-			{
-				this.dataUri = ia5;
-				++index;
-			}
-			if (seq[index] is MetaData || seq[index] is Asn1Sequence)
-			{
-				this.metaData = MetaData.GetInstance(seq[index++]);
-			}
-			if (seq[index] is Asn1OctetString octets)
-			{
-				this.content = octets;
-				++index;
-			}
-			this.temporalEvidence = Evidence.GetInstance(seq[index]);
-		}
+            int count = seq.Count, pos = 0;
+            if (count < 2 || count > 5)
+                throw new ArgumentException("Bad sequence size: " + count, nameof(seq));
 
-		public virtual DerIA5String DataUri
-		{
-			get { return dataUri; }
-		}
+            m_version = DerInteger.GetInstance(seq[pos++]);
+            m_dataUri = Asn1Utilities.ReadOptional(seq, ref pos, DerIA5String.GetOptional);
+            m_metaData = Asn1Utilities.ReadOptional(seq, ref pos, MetaData.GetOptional);
+            m_content = Asn1Utilities.ReadOptional(seq, ref pos, Asn1OctetString.GetOptional);
+            m_temporalEvidence = Evidence.GetInstance(seq[pos++]);
 
-		public MetaData MetaData
-		{
-			get { return metaData; }
-		}
+            if (pos != count)
+                throw new ArgumentException("Unexpected elements in sequence", nameof(seq));
+        }
 
-		public Asn1OctetString Content
-		{
-			get { return content; }
-		}
+        public virtual DerIA5String DataUri => m_dataUri;
 
-		public Evidence TemporalEvidence
-		{
-			get { return temporalEvidence; }
-		}
+        public MetaData MetaData => m_metaData;
+
+        public Asn1OctetString Content => m_content;
+
+        public Evidence TemporalEvidence => m_temporalEvidence;
 
 		/**
 		 * <pre>
@@ -91,9 +73,10 @@ namespace Org.BouncyCastle.Asn1.Cms
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(version);
-			v.AddOptional(dataUri, metaData, content);
-			v.Add(temporalEvidence);
+            Asn1EncodableVector v = new Asn1EncodableVector(5);
+            v.Add(m_version);
+			v.AddOptional(m_dataUri, m_metaData, m_content);
+			v.Add(m_temporalEvidence);
 			return new BerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/x509/CertificateList.cs b/crypto/src/asn1/x509/CertificateList.cs
index 5d73cf411..ecf22e372 100644
--- a/crypto/src/asn1/x509/CertificateList.cs
+++ b/crypto/src/asn1/x509/CertificateList.cs
@@ -19,24 +19,39 @@ namespace Org.BouncyCastle.Asn1.X509
     public class CertificateList
         : Asn1Encodable
     {
-        private readonly TbsCertificateList	tbsCertList;
-        private readonly AlgorithmIdentifier sigAlgID;
-        private readonly DerBitString sig;
+        public static CertificateList GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+            if (obj is CertificateList certificateList)
+                return certificateList;
+            return new CertificateList(Asn1Sequence.GetInstance(obj));
+        }
 
         public static CertificateList GetInstance(Asn1TaggedObject obj, bool explicitly)
         {
             return GetInstance(Asn1Sequence.GetInstance(obj, explicitly));
         }
 
-        public static CertificateList GetInstance(object obj)
+        public static CertificateList GetOptional(Asn1Encodable element)
         {
-            if (obj == null)
-                return null;
-            if (obj is CertificateList certificateList)
+            if (element == null)
+                throw new ArgumentNullException(nameof(element));
+
+            if (element is CertificateList certificateList)
                 return certificateList;
-            return new CertificateList(Asn1Sequence.GetInstance(obj));
+
+            Asn1Sequence asn1Sequence = Asn1Sequence.GetOptional(element);
+            if (asn1Sequence != null)
+                return new CertificateList(asn1Sequence);
+
+            return null;
         }
 
+        private readonly TbsCertificateList tbsCertList;
+        private readonly AlgorithmIdentifier sigAlgID;
+        private readonly DerBitString sig;
+
         private CertificateList(
             Asn1Sequence seq)
         {