summary refs log tree commit diff
path: root/crypto/src/asn1
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/asn1')
-rw-r--r--crypto/src/asn1/Asn1EncodableVector.cs8
-rw-r--r--crypto/src/asn1/Asn1GeneralizedTime.cs438
-rw-r--r--crypto/src/asn1/Asn1InputStream.cs4
-rw-r--r--crypto/src/asn1/Asn1OutputStream.cs22
-rw-r--r--crypto/src/asn1/Asn1RelativeOid.cs18
-rw-r--r--crypto/src/asn1/Asn1Sequence.cs30
-rw-r--r--crypto/src/asn1/Asn1Set.cs17
-rw-r--r--crypto/src/asn1/Asn1UniversalTypes.cs4
-rw-r--r--crypto/src/asn1/Asn1UtcTime.cs264
-rw-r--r--crypto/src/asn1/BEROctetStringGenerator.cs68
-rw-r--r--crypto/src/asn1/ConstructedBitStream.cs52
-rw-r--r--crypto/src/asn1/ConstructedOctetStream.cs52
-rw-r--r--crypto/src/asn1/DefiniteLengthInputStream.cs21
-rw-r--r--crypto/src/asn1/DerGeneralizedTime.cs309
-rw-r--r--crypto/src/asn1/DerOctetString.cs17
-rw-r--r--crypto/src/asn1/DerUTCTime.cs259
-rw-r--r--crypto/src/asn1/IndefiniteLengthInputStream.cs23
-rw-r--r--crypto/src/asn1/bc/BCObjectIdentifiers.cs64
-rw-r--r--crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs51
-rw-r--r--crypto/src/asn1/cmp/CertAnnContent.cs72
-rw-r--r--crypto/src/asn1/cmp/CertConfirmContent.cs35
-rw-r--r--crypto/src/asn1/cmp/CertOrEncCert.cs86
-rw-r--r--crypto/src/asn1/cmp/CertRepMessage.cs59
-rw-r--r--crypto/src/asn1/cmp/CertReqTemplateContent.cs67
-rw-r--r--crypto/src/asn1/cmp/CertResponse.cs90
-rw-r--r--crypto/src/asn1/cmp/CertStatus.cs122
-rw-r--r--crypto/src/asn1/cmp/CertifiedKeyPair.cs90
-rw-r--r--crypto/src/asn1/cmp/Challenge.cs131
-rw-r--r--crypto/src/asn1/cmp/CmpCertificate.cs93
-rw-r--r--crypto/src/asn1/cmp/CmpObjectIdentifiers.cs350
-rw-r--r--crypto/src/asn1/cmp/CrlAnnContent.cs43
-rw-r--r--crypto/src/asn1/cmp/CrlSource.cs72
-rw-r--r--crypto/src/asn1/cmp/CrlStatus.cs61
-rw-r--r--crypto/src/asn1/cmp/DhbmParameter.cs56
-rw-r--r--crypto/src/asn1/cmp/ErrorMsgContent.cs78
-rw-r--r--crypto/src/asn1/cmp/GenMsgContent.cs51
-rw-r--r--crypto/src/asn1/cmp/GenRepContent.cs46
-rw-r--r--crypto/src/asn1/cmp/InfoTypeAndValue.cs52
-rw-r--r--crypto/src/asn1/cmp/KeyRecRepContent.cs92
-rw-r--r--crypto/src/asn1/cmp/NestedMessageContent.cs35
-rw-r--r--crypto/src/asn1/cmp/OobCert.cs68
-rw-r--r--crypto/src/asn1/cmp/OobCertHash.cs65
-rw-r--r--crypto/src/asn1/cmp/PKIBody.cs192
-rw-r--r--crypto/src/asn1/cmp/PKIConfirmContent.cs34
-rw-r--r--crypto/src/asn1/cmp/PKIFailureInfo.cs8
-rw-r--r--crypto/src/asn1/cmp/PKIFreeText.cs73
-rw-r--r--crypto/src/asn1/cmp/PKIHeader.cs62
-rw-r--r--crypto/src/asn1/cmp/PKIHeaderBuilder.cs4
-rw-r--r--crypto/src/asn1/cmp/PKIMessages.cs2
-rw-r--r--crypto/src/asn1/cmp/PbmParameter.cs95
-rw-r--r--crypto/src/asn1/cmp/PollRepContent.cs111
-rw-r--r--crypto/src/asn1/cmp/PollReqContent.cs117
-rw-r--r--crypto/src/asn1/cmp/PopoDecKeyChallContent.cs35
-rw-r--r--crypto/src/asn1/cmp/PopoDecKeyRespContent.cs39
-rw-r--r--crypto/src/asn1/cmp/ProtectedPart.cs50
-rw-r--r--crypto/src/asn1/cmp/RevAnnContent.cs92
-rw-r--r--crypto/src/asn1/cmp/RevDetails.cs71
-rw-r--r--crypto/src/asn1/cmp/RevRepContent.cs84
-rw-r--r--crypto/src/asn1/cmp/RevRepContentBuilder.cs26
-rw-r--r--crypto/src/asn1/cmp/RevReqContent.cs44
-rw-r--r--crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs91
-rw-r--r--crypto/src/asn1/cms/CMSObjectIdentifiers.cs3
-rw-r--r--crypto/src/asn1/cms/KEKIdentifier.cs12
-rw-r--r--crypto/src/asn1/cms/RecipientKeyIdentifier.cs14
-rw-r--r--crypto/src/asn1/cms/Time.cs42
-rw-r--r--crypto/src/asn1/crmf/EncryptedKey.cs58
-rw-r--r--crypto/src/asn1/crmf/EncryptedValue.cs140
-rw-r--r--crypto/src/asn1/crmf/PKIPublicationInfo.cs92
-rw-r--r--crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs11
-rw-r--r--crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs8
-rw-r--r--crypto/src/asn1/esf/CrlIdentifier.cs47
-rw-r--r--crypto/src/asn1/esf/OcspIdentifier.cs25
-rw-r--r--crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs6
-rw-r--r--crypto/src/asn1/misc/MiscObjectIdentifiers.cs32
-rw-r--r--crypto/src/asn1/ocsp/CrlID.cs6
-rw-r--r--crypto/src/asn1/ocsp/ResponseData.cs10
-rw-r--r--crypto/src/asn1/ocsp/RevokedInfo.cs10
-rw-r--r--crypto/src/asn1/ocsp/SingleResponse.cs18
-rw-r--r--crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs11
-rw-r--r--crypto/src/asn1/sec/ECPrivateKeyStructure.cs50
-rw-r--r--crypto/src/asn1/tsp/TSTInfo.cs8
-rw-r--r--crypto/src/asn1/util/Asn1Dump.cs4
-rw-r--r--crypto/src/asn1/x509/AttCertValidityPeriod.cs16
-rw-r--r--crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs10
-rw-r--r--crypto/src/asn1/x509/TBSCertList.cs4
-rw-r--r--crypto/src/asn1/x509/Time.cs39
-rw-r--r--crypto/src/asn1/x509/V1TBSCertificateGenerator.cs4
-rw-r--r--crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs10
-rw-r--r--crypto/src/asn1/x509/V2TBSCertListGenerator.cs8
-rw-r--r--crypto/src/asn1/x509/V3TBSCertificateGenerator.cs4
-rw-r--r--crypto/src/asn1/x509/X509DefaultEntryConverter.cs2
-rw-r--r--crypto/src/asn1/x509/sigi/PersonalData.cs8
92 files changed, 3453 insertions, 2124 deletions
diff --git a/crypto/src/asn1/Asn1EncodableVector.cs b/crypto/src/asn1/Asn1EncodableVector.cs
index f50eb6f98..bf8d324ad 100644
--- a/crypto/src/asn1/Asn1EncodableVector.cs
+++ b/crypto/src/asn1/Asn1EncodableVector.cs
@@ -133,6 +133,14 @@ namespace Org.BouncyCastle.Asn1
             }
         }
 
+        public void AddOptionalTagged(bool isExplicit, int tagClass, int tagNo, Asn1Encodable obj)
+        {
+            if (null != obj)
+            {
+                Add(new DerTaggedObject(isExplicit, tagClass, tagNo, obj));
+            }
+        }
+
         public void AddAll(Asn1EncodableVector other)
         {
             if (null == other)
diff --git a/crypto/src/asn1/Asn1GeneralizedTime.cs b/crypto/src/asn1/Asn1GeneralizedTime.cs
new file mode 100644
index 000000000..139384c1a
--- /dev/null
+++ b/crypto/src/asn1/Asn1GeneralizedTime.cs
@@ -0,0 +1,438 @@
+using System;
+using System.Globalization;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /**
+     * Base class representing the ASN.1 GeneralizedTime type.
+     * <p>
+     * The main difference between these and UTC time is a 4 digit year.
+     * </p><p>
+     * One second resolution date+time on UTC timezone (Z)
+     * with 4 digit year (valid from 0001 to 9999).
+     * </p><p>
+     * Timestamp format is:  yyyymmddHHMMSS'Z'
+     * </p><p>
+     * <h2>X.690</h2>
+     * This is what is called "restricted string",
+     * and it uses ASCII characters to encode digits and supplemental data.
+     *
+     * <h3>11: Restrictions on BER employed by both CER and DER</h3>
+     * <h4>11.7 GeneralizedTime </h4>
+     * </p><p>
+     * <b>11.7.1</b> The encoding shall terminate with a "Z",
+     * as described in the ITU-T Rec. X.680 | ISO/IEC 8824-1 clause on
+     * GeneralizedTime.
+     * </p><p>
+     * <b>11.7.2</b> The seconds element shall always be present.
+     * </p><p>
+     * <b>11.7.3</b> The fractional-seconds elements, if present,
+     * shall omit all trailing zeros; if the elements correspond to 0,
+     * they shall be wholly omitted, and the decimal point element also
+     * shall be omitted.
+     * </p>
+     */
+    public class Asn1GeneralizedTime
+        : Asn1Object
+    {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1GeneralizedTime), Asn1Tags.GeneralizedTime) { }
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
+
+        public static Asn1GeneralizedTime GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1GeneralizedTime asn1GeneralizedTime)
+                return asn1GeneralizedTime;
+
+            if (obj is IAsn1Convertible asn1Convertible)
+            {
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1GeneralizedTime converted)
+                    return converted;
+            }
+            else if (obj is byte[] bytes)
+            {
+                try
+                {
+                    return (Asn1GeneralizedTime)Meta.Instance.FromByteArray(bytes);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message);
+                }
+            }
+
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), nameof(obj));
+        }
+
+        public static Asn1GeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            return (Asn1GeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
+
+        internal readonly byte[] m_contents;
+
+        public Asn1GeneralizedTime(string time)
+        {
+            m_contents = Strings.ToByteArray(time);
+
+            try
+            {
+                ToDateTime();
+            }
+            catch (FormatException e)
+            {
+                throw new ArgumentException("invalid date string: " + e.Message);
+            }
+        }
+
+        public Asn1GeneralizedTime(DateTime time)
+        {
+            DateTime utc = time.ToUniversalTime();
+            var formatStr = @"yyyyMMddHHmmss\Z";
+            var formatProvider = DateTimeFormatInfo.InvariantInfo;
+            string utcString = utc.ToString(formatStr, formatProvider);
+            m_contents = Strings.ToByteArray(utcString);
+        }
+
+        // TODO Custom locale constructor?
+        //public Asn1GeneralizedTime(DateTime time, Locale locale)
+        //{
+        //    SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss\Z", locale);
+
+        //    dateF.setTimeZone(new SimpleTimeZone(0, "Z"));
+
+        //    this.contents = Strings.toByteArray(dateF.format(time));
+        //}
+
+        internal Asn1GeneralizedTime(byte[] bytes)
+        {
+            if (bytes == null)
+                throw new ArgumentNullException(nameof(bytes));
+            if (bytes.Length < 4)
+                throw new ArgumentException("GeneralizedTime string too short", nameof(bytes));
+
+            m_contents = bytes;
+
+            if (!(IsDigit(0) && IsDigit(1) && IsDigit(2) && IsDigit(3)))
+                throw new ArgumentException("illegal characters in GeneralizedTime string", nameof(bytes));
+        }
+
+        public string TimeString => Strings.FromByteArray(m_contents);
+
+        public string GetTime()
+        {
+            string stime = Strings.FromByteArray(m_contents);
+
+            //
+            // standardise the format.
+            //
+            if (stime[stime.Length - 1] == 'Z')
+                return stime.Substring(0, stime.Length - 1) + "GMT+00:00";
+
+            int signPos = stime.Length - 6;
+            char sign = stime[signPos];
+            if ((sign == '-' || sign == '+') && stime.IndexOf("GMT") == signPos - 3)
+            {
+                // already a GMT string!
+                return stime;
+            }
+
+            signPos = stime.Length - 5;
+            sign = stime[signPos];
+            if (sign == '-' || sign == '+')
+            {
+                return stime.Substring(0, signPos)
+                    + "GMT"
+                    + stime.Substring(signPos, 3)
+                    + ":"
+                    + stime.Substring(signPos + 3);
+            }
+
+            signPos = stime.Length - 3;
+            sign = stime[signPos];
+            if (sign == '-' || sign == '+')
+            {
+                return stime.Substring(0, signPos)
+                    + "GMT"
+                    + stime.Substring(signPos)
+                    + ":00";
+            }
+
+            return stime + CalculateGmtOffset(stime);
+        }
+
+        private string CalculateGmtOffset(string stime)
+        {
+            TimeZoneInfo timeZone = TimeZoneInfo.Local;
+            TimeSpan offset = timeZone.BaseUtcOffset;
+
+            string sign = "+";
+            if (offset.CompareTo(TimeSpan.Zero) < 0)
+            {
+                sign = "-";
+                offset = offset.Duration();
+            }
+
+            int hours = offset.Hours;
+            int minutes = offset.Minutes;
+
+            try
+            {
+                if (timeZone.SupportsDaylightSavingTime)
+                {
+                    string d = stime + "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
+                    string formatStr = CalculateGmtFormatString(d);
+
+                    DateTime dateTime = ParseDateString(d, formatStr, makeUniversal: true);
+
+                    if (timeZone.IsDaylightSavingTime(dateTime))
+                    {
+                        hours += sign.Equals("+") ? 1 : -1;
+                    }
+                }
+            }
+            catch (Exception)
+            {
+                // we'll do our best and ignore daylight savings
+            }
+
+            return "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
+        }
+
+        private string CalculateGmtFormatString(string d)
+        {
+            if (HasFractionalSeconds())
+            {
+                int fCount = Platform.IndexOf(d, "GMT") - 1 - d.IndexOf('.');
+                return @"yyyyMMddHHmmss." + FString(fCount) + @"'GMT'zzz";
+            }
+
+            if (HasSeconds())
+                return @"yyyyMMddHHmmss'GMT'zzz";
+
+            if (HasMinutes())
+                return @"yyyyMMddHHmm'GMT'zzz";
+
+            return @"yyyyMMddHH'GMT'zzz";
+        }
+
+        private string Convert(int time)
+        {
+            if (time < 10)
+                return "0" + time;
+
+            return time.ToString();
+        }
+
+        public DateTime ToDateTime()
+        {
+            string formatStr;
+            string stime = Strings.FromByteArray(m_contents);
+            string d = stime;
+            bool makeUniversal = false;
+
+            if (Platform.EndsWith(stime, "Z"))
+            {
+                if (HasFractionalSeconds())
+                {
+                    int fCount = d.Length - d.IndexOf('.') - 2;
+                    formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z";
+                }
+                else if (HasSeconds())
+                {
+                    formatStr = @"yyyyMMddHHmmss\Z";
+                }
+                else if (HasMinutes())
+                {
+                    formatStr = @"yyyyMMddHHmm\Z";
+                }
+                else
+                {
+                    formatStr = @"yyyyMMddHH\Z";
+                }
+            }
+            else if (stime.IndexOf('-') > 0 || stime.IndexOf('+') > 0)
+            {
+                d = GetTime();
+                formatStr = CalculateGmtFormatString(d);
+                makeUniversal = true;
+            }
+            else
+            {
+                if (HasFractionalSeconds())
+                {
+                    int fCount = d.Length - 1 - d.IndexOf('.');
+                    formatStr = @"yyyyMMddHHmmss." + FString(fCount);
+                }
+                else if (HasSeconds())
+                {
+                    formatStr = @"yyyyMMddHHmmss";
+                }
+                else if (HasMinutes())
+                {
+                    formatStr = @"yyyyMMddHHmm";
+                }
+                else
+                {
+                    formatStr = @"yyyyMMddHH";
+                }
+            }
+
+            // TODO Epoch adjustment?
+            //return DateUtil.epochAdjust(dateF.parse(d));
+            return ParseDateString(d, formatStr, makeUniversal);
+        }
+
+        protected bool HasFractionalSeconds()
+        {
+            return m_contents.Length > 14 && m_contents[14] == '.';
+        }
+
+        protected bool HasSeconds()
+        {
+            return IsDigit(12) && IsDigit(13);
+        }
+
+        protected bool HasMinutes()
+        {
+            return IsDigit(10) && IsDigit(11);
+        }
+
+        private bool IsDigit(int pos)
+        {
+            return m_contents.Length > pos && m_contents[pos] >= '0' && m_contents[pos] <= '9';
+        }
+
+        internal override IAsn1Encoding GetEncoding(int encoding)
+        {
+            if (Asn1OutputStream.EncodingDer == encoding)
+                return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetDerTime());
+
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, m_contents);
+        }
+
+        internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
+        {
+            if (Asn1OutputStream.EncodingDer == encoding)
+                return new PrimitiveEncoding(tagClass, tagNo, GetDerTime());
+
+            return new PrimitiveEncoding(tagClass, tagNo, m_contents);
+        }
+
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+        {
+            if (!(asn1Object is Asn1GeneralizedTime that))
+                return false;
+
+            return Arrays.AreEqual(m_contents, that.m_contents);
+        }
+
+        protected override int Asn1GetHashCode()
+        {
+            return Arrays.GetHashCode(m_contents);
+        }
+
+        internal static Asn1GeneralizedTime CreatePrimitive(byte[] contents)
+        {
+            return new Asn1GeneralizedTime(contents);
+        }
+
+        internal byte[] GetDerTime()
+        {
+            if (m_contents[m_contents.Length - 1] != 'Z')
+            {
+                return m_contents; // TODO: is there a better way?
+            }
+
+            if (!HasMinutes())
+            {
+                byte[] derTime = new byte[m_contents.Length + 4];
+
+                Array.Copy(m_contents, 0, derTime, 0, m_contents.Length - 1);
+                Array.Copy(Strings.ToByteArray("0000Z"), 0, derTime, m_contents.Length - 1, 5);
+
+                return derTime;
+            }
+            else if (!HasSeconds())
+            {
+                byte[] derTime = new byte[m_contents.Length + 2];
+
+                Array.Copy(m_contents, 0, derTime, 0, m_contents.Length - 1);
+                Array.Copy(Strings.ToByteArray("00Z"), 0, derTime, m_contents.Length - 1, 3);
+
+                return derTime;
+            }
+            else if (HasFractionalSeconds())
+            {
+                int ind = m_contents.Length - 2;
+                while (ind > 0 && m_contents[ind] == '0')
+                {
+                    ind--;
+                }
+
+                if (m_contents[ind] == '.')
+                {
+                    byte[] derTime = new byte[ind + 1];
+
+                    Array.Copy(m_contents, 0, derTime, 0, ind);
+                    derTime[ind] = (byte)'Z';
+
+                    return derTime;
+                }
+                else
+                {
+                    byte[] derTime = new byte[ind + 2];
+
+                    Array.Copy(m_contents, 0, derTime, 0, ind + 1);
+                    derTime[ind + 1] = (byte)'Z';
+
+                    return derTime;
+                }
+            }
+            else
+            {
+                return m_contents;
+            }
+        }
+
+        private static string FString(int count)
+        {
+            StringBuilder sb = new StringBuilder();
+            for (int i = 0; i < count; ++i)
+            {
+                sb.Append('f');
+            }
+            return sb.ToString();
+        }
+
+        private static DateTime ParseDateString(string s, string format, bool makeUniversal)
+        {
+            DateTimeStyles dateTimeStyles = DateTimeStyles.None;
+            if (Platform.EndsWith(format, "Z"))
+            {
+                dateTimeStyles |= DateTimeStyles.AdjustToUniversal;
+                dateTimeStyles |= DateTimeStyles.AssumeUniversal;
+            }
+
+            DateTime dt = DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, dateTimeStyles);
+
+            return makeUniversal ? dt.ToUniversalTime() : dt;
+        }
+    }
+}
diff --git a/crypto/src/asn1/Asn1InputStream.cs b/crypto/src/asn1/Asn1InputStream.cs
index aa91cdf62..0e772010f 100644
--- a/crypto/src/asn1/Asn1InputStream.cs
+++ b/crypto/src/asn1/Asn1InputStream.cs
@@ -426,7 +426,7 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.BitString:
                 return DerBitString.CreatePrimitive(bytes);
             case Asn1Tags.GeneralizedTime:
-                return DerGeneralizedTime.CreatePrimitive(bytes);
+                return Asn1GeneralizedTime.CreatePrimitive(bytes);
             case Asn1Tags.GeneralString:
                 return DerGeneralString.CreatePrimitive(bytes);
             case Asn1Tags.GraphicString:
@@ -452,7 +452,7 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.UniversalString:
                 return DerUniversalString.CreatePrimitive(bytes);
             case Asn1Tags.UtcTime:
-                return DerUtcTime.CreatePrimitive(bytes);
+                return Asn1UtcTime.CreatePrimitive(bytes);
             case Asn1Tags.Utf8String:
                 return DerUtf8String.CreatePrimitive(bytes);
             case Asn1Tags.VideotexString:
diff --git a/crypto/src/asn1/Asn1OutputStream.cs b/crypto/src/asn1/Asn1OutputStream.cs
index 1a69d7c2c..163e3848c 100644
--- a/crypto/src/asn1/Asn1OutputStream.cs
+++ b/crypto/src/asn1/Asn1OutputStream.cs
@@ -1,5 +1,10 @@
 using System;
 using System.IO;
+using System.Diagnostics;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+using System.Buffers.Binary;
+using System.Numerics;
+#endif
 
 using Org.BouncyCastle.Utilities.IO;
 
@@ -73,10 +78,18 @@ namespace Org.BouncyCastle.Asn1
         {
             if (dl < 128)
             {
+                Debug.Assert(dl >= 0);
                 WriteByte((byte)dl);
                 return;
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> encoding = stackalloc byte[5];
+            BinaryPrimitives.WriteUInt32BigEndian(encoding[1..], (uint)dl);
+            int leadingZeroBytes = BitOperations.LeadingZeroCount((uint)dl) / 8;
+            encoding[leadingZeroBytes] = (byte)(0x84 - leadingZeroBytes);
+            Write(encoding[leadingZeroBytes..]);
+#else
             byte[] stack = new byte[5];
             int pos = stack.Length;
 
@@ -91,6 +104,7 @@ namespace Org.BouncyCastle.Asn1
             stack[--pos] = (byte)(0x80 | count);
 
             Write(stack, pos, count + 1);
+#endif
         }
 
         internal void WriteIdentifier(int tagClass, int tagNo)
@@ -101,7 +115,11 @@ namespace Org.BouncyCastle.Asn1
                 return;
             }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> stack = stackalloc byte[6];
+#else
             byte[] stack = new byte[6];
+#endif
             int pos = stack.Length;
 
             stack[--pos] = (byte)(tagNo & 0x7F);
@@ -113,7 +131,11 @@ namespace Org.BouncyCastle.Asn1
 
             stack[--pos] = (byte)(tagClass | 0x1F);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Write(stack[pos..]);
+#else
             Write(stack, pos, stack.Length - pos);
+#endif
         }
 
         internal static IAsn1Encoding[] GetContentsEncodings(int encoding, Asn1Encodable[] elements)
diff --git a/crypto/src/asn1/Asn1RelativeOid.cs b/crypto/src/asn1/Asn1RelativeOid.cs
index d3c031913..a1997864d 100644
--- a/crypto/src/asn1/Asn1RelativeOid.cs
+++ b/crypto/src/asn1/Asn1RelativeOid.cs
@@ -196,7 +196,11 @@ namespace Org.BouncyCastle.Asn1
 
         internal static void WriteField(Stream outputStream, long fieldValue)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            Span<byte> result = stackalloc byte[9];
+#else
             byte[] result = new byte[9];
+#endif
             int pos = 8;
             result[pos] = (byte)((int)fieldValue & 0x7F);
             while (fieldValue >= (1L << 7))
@@ -204,7 +208,11 @@ namespace Org.BouncyCastle.Asn1
                 fieldValue >>= 7;
                 result[--pos] = (byte)((int)fieldValue | 0x80);
             }
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            outputStream.Write(result[pos..]);
+#else
             outputStream.Write(result, pos, 9 - pos);
+#endif
         }
 
         internal static void WriteField(Stream outputStream, BigInteger fieldValue)
@@ -217,14 +225,24 @@ namespace Org.BouncyCastle.Asn1
             else
             {
                 BigInteger tmpValue = fieldValue;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                Span<byte> tmp = byteCount <= 16
+                    ? stackalloc byte[byteCount]
+                    : new byte[byteCount];
+#else
                 byte[] tmp = new byte[byteCount];
+#endif
                 for (int i = byteCount - 1; i >= 0; i--)
                 {
                     tmp[i] = (byte)(tmpValue.IntValue | 0x80);
                     tmpValue = tmpValue.ShiftRight(7);
                 }
                 tmp[byteCount - 1] &= 0x7F;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                outputStream.Write(tmp);
+#else
                 outputStream.Write(tmp, 0, tmp.Length);
+#endif
             }
         }
 
diff --git a/crypto/src/asn1/Asn1Sequence.cs b/crypto/src/asn1/Asn1Sequence.cs
index 3ceea4521..1a123e26d 100644
--- a/crypto/src/asn1/Asn1Sequence.cs
+++ b/crypto/src/asn1/Asn1Sequence.cs
@@ -195,6 +195,18 @@ namespace Org.BouncyCastle.Asn1
             get { return elements.Length; }
         }
 
+        public virtual T[] MapElements<T>(Func<Asn1Encodable, T> func)
+        {
+            // NOTE: Call Count here to 'force' a LazyDerSequence
+            int count = Count;
+            T[] result = new T[count];
+            for (int i = 0; i < count; ++i)
+            {
+                result[i] = func(elements[i]);
+            }
+            return result;
+        }
+
         public virtual Asn1Encodable[] ToArray()
         {
             return Asn1EncodableVector.CloneElements(elements);
@@ -246,26 +258,12 @@ namespace Org.BouncyCastle.Asn1
         // TODO[asn1] Preferably return an Asn1BitString[] (doesn't exist yet)
         internal DerBitString[] GetConstructedBitStrings()
         {
-            // NOTE: Call Count here to 'force' a LazyDerSequence
-            int count = Count;
-            DerBitString[] bitStrings = new DerBitString[count];
-            for (int i = 0; i < count; ++i)
-            {
-                bitStrings[i] = DerBitString.GetInstance(elements[i]);
-            }
-            return bitStrings;
+            return MapElements(DerBitString.GetInstance);
         }
 
         internal Asn1OctetString[] GetConstructedOctetStrings()
         {
-            // NOTE: Call Count here to 'force' a LazyDerSequence
-            int count = Count;
-            Asn1OctetString[] octetStrings = new Asn1OctetString[count];
-            for (int i = 0; i < count; ++i)
-            {
-                octetStrings[i] = Asn1OctetString.GetInstance(elements[i]);
-            }
-            return octetStrings;
+            return MapElements(Asn1OctetString.GetInstance);
         }
 
         // TODO[asn1] Preferably return an Asn1BitString (doesn't exist yet)
diff --git a/crypto/src/asn1/Asn1Set.cs b/crypto/src/asn1/Asn1Set.cs
index 1045b7f74..faec50eb0 100644
--- a/crypto/src/asn1/Asn1Set.cs
+++ b/crypto/src/asn1/Asn1Set.cs
@@ -162,6 +162,18 @@ namespace Org.BouncyCastle.Asn1
             get { return elements.Length; }
         }
 
+        public virtual T[] MapElements<T>(Func<Asn1Encodable, T> func)
+        {
+            // NOTE: Call Count here to 'force' a LazyDerSet
+            int count = Count;
+            T[] result = new T[count];
+            for (int i = 0; i < count; ++i)
+            {
+                result[i] = func(elements[i]);
+            }
+            return result;
+        }
+
         public virtual Asn1Encodable[] ToArray()
         {
             return Asn1EncodableVector.CloneElements(elements);
@@ -294,6 +306,10 @@ namespace Org.BouncyCastle.Asn1
                 if (a0 != b0)
                     return a0 < b0 ? -1 : 1;
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                int compareLength = System.Math.Min(a.Length, b.Length) - 1;
+                return a.AsSpan(1, compareLength).SequenceCompareTo(b.AsSpan(1, compareLength));
+#else
                 int len = System.Math.Min(a.Length, b.Length);
                 for (int i = 1; i < len; ++i)
                 {
@@ -303,6 +319,7 @@ namespace Org.BouncyCastle.Asn1
                 }
                 Debug.Assert(a.Length == b.Length);
                 return 0;
+#endif
             }
         }
     }
diff --git a/crypto/src/asn1/Asn1UniversalTypes.cs b/crypto/src/asn1/Asn1UniversalTypes.cs
index 214918bcd..f5b5d0498 100644
--- a/crypto/src/asn1/Asn1UniversalTypes.cs
+++ b/crypto/src/asn1/Asn1UniversalTypes.cs
@@ -49,9 +49,9 @@ namespace Org.BouncyCastle.Asn1
             case Asn1Tags.IA5String:                // [UNIVERSAL 22] IMPLICIT OCTET STRING (encode as if)
                 return DerIA5String.Meta.Instance;
             case Asn1Tags.UtcTime:                  // [UNIVERSAL 23] IMPLICIT VisibleString (restricted values)
-                return DerUtcTime.Meta.Instance;
+                return Asn1UtcTime.Meta.Instance;
             case Asn1Tags.GeneralizedTime:          // [UNIVERSAL 24] IMPLICIT VisibleString (restricted values)
-                return DerGeneralizedTime.Meta.Instance;
+                return Asn1GeneralizedTime.Meta.Instance;
             case Asn1Tags.GraphicString:            // [UNIVERSAL 25] IMPLICIT OCTET STRING (encode as if)
                 return DerGraphicString.Meta.Instance;
             case Asn1Tags.VisibleString:            // [UNIVERSAL 26] IMPLICIT OCTET STRING (encode as if)
diff --git a/crypto/src/asn1/Asn1UtcTime.cs b/crypto/src/asn1/Asn1UtcTime.cs
new file mode 100644
index 000000000..05de430c4
--- /dev/null
+++ b/crypto/src/asn1/Asn1UtcTime.cs
@@ -0,0 +1,264 @@
+using System;
+using System.Globalization;
+using System.IO;
+
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1
+{
+    /**
+     * UTC time object.
+     */
+    public class Asn1UtcTime
+        : Asn1Object
+    {
+        internal class Meta : Asn1UniversalType
+        {
+            internal static readonly Asn1UniversalType Instance = new Meta();
+
+            private Meta() : base(typeof(Asn1UtcTime), Asn1Tags.UtcTime) {}
+
+            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
+            {
+                return CreatePrimitive(octetString.GetOctets());
+            }
+        }
+
+		/**
+         * return a UTC Time from the passed in object.
+         *
+         * @exception ArgumentException if the object cannot be converted.
+         */
+        public static Asn1UtcTime GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+
+            if (obj is Asn1UtcTime asn1UtcTime)
+                return asn1UtcTime;
+
+            if (obj is IAsn1Convertible asn1Convertible)
+            {
+                Asn1Object asn1Object = asn1Convertible.ToAsn1Object();
+                if (asn1Object is Asn1UtcTime converted)
+                    return converted;
+            }
+            else if (obj is byte[] bytes)
+            {
+                try
+                {
+                    return (Asn1UtcTime)Meta.Instance.FromByteArray(bytes);
+                }
+                catch (IOException e)
+                {
+                    throw new ArgumentException("failed to construct UTC time from byte[]: " + e.Message);
+                }
+            }
+
+            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
+        }
+
+        /**
+         * return a UTC Time from a tagged object.
+         *
+         * @param taggedObject the tagged object holding the object we want
+         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
+         * @exception ArgumentException if the tagged object cannot be converted.
+         */
+        public static Asn1UtcTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            return (Asn1UtcTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
+        }
+
+        private readonly string time;
+
+        /**
+         * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
+         * never encoded. When you're creating one of these objects from scratch, that's
+         * what you want to use, otherwise we'll try to deal with whatever Gets read from
+         * the input stream... (this is why the input format is different from the GetTime()
+         * method output).
+         * <p>
+         * @param time the time string.</p>
+         */
+        public Asn1UtcTime(string time)
+        {
+			if (time == null)
+				throw new ArgumentNullException("time");
+
+			this.time = time;
+
+			try
+			{
+				ToDateTime();
+			}
+			catch (FormatException e)
+			{
+				throw new ArgumentException("invalid date string: " + e.Message);
+			}
+        }
+
+		/**
+         * base constructor from a DateTime object
+         */
+        public Asn1UtcTime(DateTime time)
+        {
+            this.time = time.ToUniversalTime().ToString("yyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
+        }
+
+		internal Asn1UtcTime(byte[] contents)
+        {
+            //
+            // explicitly convert to characters
+            //
+            this.time = Strings.FromAsciiByteArray(contents);
+        }
+
+		/**
+		 * return the time as a date based on whatever a 2 digit year will return. For
+		 * standardised processing use ToAdjustedDateTime().
+		 *
+		 * @return the resulting date
+		 * @exception ParseException if the date string cannot be parsed.
+		 */
+		public DateTime ToDateTime()
+		{
+			return ParseDateString(TimeString, @"yyMMddHHmmss'GMT'zzz");
+		}
+
+		/**
+		* return the time as an adjusted date
+		* in the range of 1950 - 2049.
+		*
+		* @return a date in the range of 1950 to 2049.
+		* @exception ParseException if the date string cannot be parsed.
+		*/
+		public DateTime ToAdjustedDateTime()
+		{
+			return ParseDateString(AdjustedTimeString, @"yyyyMMddHHmmss'GMT'zzz");
+		}
+
+		private DateTime ParseDateString(string dateStr, string formatStr)
+		{
+			DateTime dt = DateTime.ParseExact(
+				dateStr,
+				formatStr,
+				DateTimeFormatInfo.InvariantInfo);
+
+			return dt.ToUniversalTime();
+		}
+
+		/**
+         * return the time - always in the form of
+         *  YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
+         * <p>
+         * Normally in a certificate we would expect "Z" rather than "GMT",
+         * however adding the "GMT" means we can just use:
+         * <pre>
+         *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
+         * </pre>
+         * To read in the time and Get a date which is compatible with our local
+         * time zone.</p>
+         * <p>
+         * <b>Note:</b> In some cases, due to the local date processing, this
+         * may lead to unexpected results. If you want to stick the normal
+         * convention of 1950 to 2049 use the GetAdjustedTime() method.</p>
+         */
+        public string TimeString
+        {
+			get
+			{
+				//
+				// standardise the format.
+				//
+				if (time.IndexOf('-') < 0 && time.IndexOf('+') < 0)
+				{
+					if (time.Length == 11)
+					{
+						return time.Substring(0, 10) + "00GMT+00:00";
+					}
+					else
+					{
+						return time.Substring(0, 12) + "GMT+00:00";
+					}
+				}
+				else
+				{
+					int index = time.IndexOf('-');
+					if (index < 0)
+					{
+						index = time.IndexOf('+');
+					}
+					string d = time;
+
+					if (index == time.Length - 3)
+					{
+						d += "00";
+					}
+
+					if (index == 10)
+					{
+						return d.Substring(0, 10) + "00GMT" + d.Substring(10, 3) + ":" + d.Substring(13, 2);
+					}
+					else
+					{
+						return d.Substring(0, 12) + "GMT" + d.Substring(12, 3) + ":" +  d.Substring(15, 2);
+					}
+				}
+			}
+        }
+
+		/// <summary>
+		/// Return a time string as an adjusted date with a 4 digit year.
+		/// This goes in the range of 1950 - 2049.
+		/// </summary>
+		public string AdjustedTimeString
+		{
+			get
+			{
+				string d = TimeString;
+				string c = d[0] < '5' ? "20" : "19";
+
+				return c + d;
+			}
+		}
+
+        internal byte[] GetOctets()
+        {
+            return Strings.ToAsciiByteArray(time);
+        }
+
+        internal override IAsn1Encoding GetEncoding(int encoding)
+        {
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UtcTime, GetOctets());
+        }
+
+        internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
+        {
+            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
+        }
+
+        protected override bool Asn1Equals(Asn1Object asn1Object)
+		{
+            if (!(asn1Object is Asn1UtcTime that))
+                return false;
+
+            return this.time == that.time;
+        }
+
+		protected override int Asn1GetHashCode()
+		{
+            return time.GetHashCode();
+        }
+
+		public override string ToString()
+		{
+			return time;
+		}
+
+        internal static Asn1UtcTime CreatePrimitive(byte[] contents)
+        {
+            return new Asn1UtcTime(contents);
+        }
+    }
+}
diff --git a/crypto/src/asn1/BEROctetStringGenerator.cs b/crypto/src/asn1/BEROctetStringGenerator.cs
index 7893139d6..b07576e7d 100644
--- a/crypto/src/asn1/BEROctetStringGenerator.cs
+++ b/crypto/src/asn1/BEROctetStringGenerator.cs
@@ -64,6 +64,9 @@ namespace Org.BouncyCastle.Asn1
 			{
 				Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+                Write(buffer.AsSpan(offset, count));
+#else
                 int bufLen = _buf.Length;
                 int available = bufLen - _off;
                 if (count < available)
@@ -77,8 +80,9 @@ namespace Org.BouncyCastle.Asn1
                 if (_off > 0)
                 {
                     Array.Copy(buffer, offset, _buf, _off, available);
-                    pos += available;
+                    pos = available;
                     DerOctetString.Encode(_derOut, _buf, 0, bufLen);
+                    //_off = 0;
                 }
 
                 int remaining;
@@ -90,9 +94,40 @@ namespace Org.BouncyCastle.Asn1
 
                 Array.Copy(buffer, offset + pos, _buf, 0, remaining);
                 this._off = remaining;
+#endif
+            }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            public override void Write(ReadOnlySpan<byte> buffer)
+			{
+                int bufLen = _buf.Length;
+                int available = bufLen - _off;
+                if (buffer.Length < available)
+                {
+                    buffer.CopyTo(_buf.AsSpan(_off));
+                    _off += buffer.Length;
+                    return;
+                }
+
+                if (_off > 0)
+                {
+                    DerOctetString.Encode(_derOut, _buf.AsSpan(0, _off), buffer[..available]);
+                    buffer = buffer[available..];
+                    //_off = 0;
+                }
+
+                while (buffer.Length >= bufLen)
+                {
+                    DerOctetString.Encode(_derOut, buffer[..bufLen]);
+                    buffer = buffer[bufLen..];
+                }
+
+                buffer.CopyTo(_buf.AsSpan());
+                _off = buffer.Length;
             }
+#endif
 
-			public override void WriteByte(byte value)
+            public override void WriteByte(byte value)
 			{
 				_buf[_off++] = value;
 
@@ -103,33 +138,20 @@ namespace Org.BouncyCastle.Asn1
 				}
 			}
 
-#if PORTABLE
             protected override void Dispose(bool disposing)
             {
                 if (disposing)
                 {
-                    ImplClose();
-                }
-                base.Dispose(disposing);
-            }
-#else
-            public override void Close()
-            {
-                ImplClose();
-                base.Close();
-            }
-#endif
-
-            private void ImplClose()
-            {
-                if (_off != 0)
-                {
-                    DerOctetString.Encode(_derOut, _buf, 0, _off);
-                }
+                    if (_off != 0)
+                    {
+                        DerOctetString.Encode(_derOut, _buf, 0, _off);
+                    }
 
-                _derOut.FlushInternal();
+                    _derOut.FlushInternal();
 
-                _gen.WriteBerEnd();
+                    _gen.WriteBerEnd();
+                }
+                base.Dispose(disposing);
             }
         }
     }
diff --git a/crypto/src/asn1/ConstructedBitStream.cs b/crypto/src/asn1/ConstructedBitStream.cs
index 49f54fc1b..f089dac75 100644
--- a/crypto/src/asn1/ConstructedBitStream.cs
+++ b/crypto/src/asn1/ConstructedBitStream.cs
@@ -33,6 +33,9 @@ namespace Org.BouncyCastle.Asn1
         {
             Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Read(buffer.AsSpan(offset, count));
+#else
             if (count < 1)
                 return 0;
 
@@ -75,8 +78,57 @@ namespace Org.BouncyCastle.Asn1
                     m_currentStream = m_currentParser.GetBitStream();
                 }
             }
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            if (buffer.IsEmpty)
+                return 0;
+
+            if (m_currentStream == null)
+            {
+                if (!m_first)
+                    return 0;
+
+                m_currentParser = GetNextParser();
+                if (m_currentParser == null)
+                    return 0;
+
+                m_first = false;
+                m_currentStream = m_currentParser.GetBitStream();
+            }
+
+            int totalRead = 0;
+
+            for (;;)
+            {
+                int numRead = m_currentStream.Read(buffer[totalRead..]);
+
+                if (numRead > 0)
+                {
+                    totalRead += numRead;
+
+                    if (totalRead == buffer.Length)
+                        return totalRead;
+                }
+                else
+                {
+                    m_padBits = m_currentParser.PadBits;
+                    m_currentParser = GetNextParser();
+                    if (m_currentParser == null)
+                    {
+                        m_currentStream = null;
+                        return totalRead;
+                    }
+
+                    m_currentStream = m_currentParser.GetBitStream();
+                }
+            }
+        }
+#endif
+
         public override int ReadByte()
         {
             if (m_currentStream == null)
diff --git a/crypto/src/asn1/ConstructedOctetStream.cs b/crypto/src/asn1/ConstructedOctetStream.cs
index 12aa14e74..d005f9fe7 100644
--- a/crypto/src/asn1/ConstructedOctetStream.cs
+++ b/crypto/src/asn1/ConstructedOctetStream.cs
@@ -1,3 +1,4 @@
+using System;
 using System.IO;
 
 using Org.BouncyCastle.Utilities;
@@ -22,6 +23,9 @@ namespace Org.BouncyCastle.Asn1
 		{
 			Streams.ValidateBufferArguments(buffer, offset, count);
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			return Read(buffer.AsSpan(offset, count));
+#else
 			if (count < 1)
                 return 0;
 
@@ -63,8 +67,56 @@ namespace Org.BouncyCastle.Asn1
 					m_currentStream = next.GetOctetStream();
 				}
 			}
+#endif
 		}
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		public override int Read(Span<byte> buffer)
+		{
+			if (buffer.IsEmpty)
+                return 0;
+
+			if (m_currentStream == null)
+			{
+				if (!m_first)
+					return 0;
+
+                Asn1OctetStringParser next = GetNextParser();
+                if (next == null)
+                    return 0;
+
+				m_first = false;
+				m_currentStream = next.GetOctetStream();
+			}
+
+			int totalRead = 0;
+
+			for (;;)
+			{
+				int numRead = m_currentStream.Read(buffer[totalRead..]);
+
+				if (numRead > 0)
+				{
+					totalRead += numRead;
+
+					if (totalRead == buffer.Length)
+						return totalRead;
+				}
+				else
+				{
+                    Asn1OctetStringParser next = GetNextParser();
+                    if (next == null)
+					{
+						m_currentStream = null;
+						return totalRead;
+					}
+
+					m_currentStream = next.GetOctetStream();
+				}
+			}
+		}
+#endif
+
 		public override int ReadByte()
 		{
 			if (m_currentStream == null)
diff --git a/crypto/src/asn1/DefiniteLengthInputStream.cs b/crypto/src/asn1/DefiniteLengthInputStream.cs
index ed5bd2446..89f0d5a62 100644
--- a/crypto/src/asn1/DefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/DefiniteLengthInputStream.cs
@@ -79,6 +79,27 @@ namespace Org.BouncyCastle.Asn1
             return numRead;
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            if (_remaining == 0)
+                return 0;
+
+            int toRead = System.Math.Min(buffer.Length, _remaining);
+            int numRead = _in.Read(buffer[..toRead]);
+
+            if (numRead < 1)
+                throw new EndOfStreamException("DEF length " + _originalLength + " object truncated by " + _remaining);
+
+            if ((_remaining -= numRead) == 0)
+            {
+                SetParentEofDetect();
+            }
+
+            return numRead;
+        }
+#endif
+
         internal void ReadAllIntoByteArray(byte[] buf)
         {
             if (_remaining != buf.Length)
diff --git a/crypto/src/asn1/DerGeneralizedTime.cs b/crypto/src/asn1/DerGeneralizedTime.cs
index 16774cb02..0386ecb02 100644
--- a/crypto/src/asn1/DerGeneralizedTime.cs
+++ b/crypto/src/asn1/DerGeneralizedTime.cs
@@ -1,326 +1,33 @@
 using System;
-using System.Globalization;
-using System.IO;
-using System.Text;
-
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1
 {
-    /**
-     * Generalized time object.
-     */
     public class DerGeneralizedTime
-        : Asn1Object
+        : Asn1GeneralizedTime
     {
-        internal class Meta : Asn1UniversalType
-        {
-            internal static readonly Asn1UniversalType Instance = new Meta();
-
-            private Meta() : base(typeof(DerGeneralizedTime), Asn1Tags.GeneralizedTime) {}
-
-            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
-            {
-                return CreatePrimitive(octetString.GetOctets());
-            }
-        }
-
-        /**
-         * return a generalized time from the passed in object
-         *
-         * @exception ArgumentException if the object cannot be converted.
-         */
-        public static DerGeneralizedTime GetInstance(object obj)
-        {
-            if (obj == null || obj is DerGeneralizedTime)
-            {
-                return (DerGeneralizedTime)obj;
-            }
-            else if (obj is IAsn1Convertible)
-            {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerGeneralizedTime)
-                    return (DerGeneralizedTime)asn1Object;
-            }
-            else if (obj is byte[])
-            {
-                try
-                {
-                    return (DerGeneralizedTime)Meta.Instance.FromByteArray((byte[])obj);
-                }
-                catch (IOException e)
-                {
-                    throw new ArgumentException("failed to construct generalized time from byte[]: " + e.Message);
-                }
-            }
-
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj), "obj");
-        }
-
-        /**
-         * return a generalized Time object from a tagged object.
-         *
-         * @param taggedObject the tagged object holding the object we want
-         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot be converted.
-         */
-        public static DerGeneralizedTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
-        {
-            return (DerGeneralizedTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
-        }
-
-        private readonly string time;
-
-        /**
-         * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z
-         * for local time, or Z+-HHMM on the end, for difference between local
-         * time and UTC time. The fractional second amount f must consist of at
-         * least one number with trailing zeroes removed.
-         *
-         * @param time the time string.
-         * @exception ArgumentException if string is an illegal format.
-         */
-        public DerGeneralizedTime(
-            string time)
+        public DerGeneralizedTime(byte[] time)
+            : base(time)
         {
-            this.time = time;
-
-            try
-            {
-                ToDateTime();
-            }
-            catch (FormatException e)
-            {
-                throw new ArgumentException("invalid date string: " + e.Message);
-            }
         }
 
-        /**
-         * base constructor from a local time object
-         */
         public DerGeneralizedTime(DateTime time)
+            : base(time)
         {
-            this.time = time.ToUniversalTime().ToString(@"yyyyMMddHHmmss\Z");
-        }
-
-        internal DerGeneralizedTime(
-            byte[] bytes)
-        {
-            //
-            // explicitly convert to characters
-            //
-            this.time = Strings.FromAsciiByteArray(bytes);
-        }
-
-        /**
-         * Return the time.
-         * @return The time string as it appeared in the encoded object.
-         */
-        public string TimeString
-        {
-            get { return time; }
-        }
-
-        /**
-         * return the time - always in the form of
-         *  YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm).
-         * <p>
-         * Normally in a certificate we would expect "Z" rather than "GMT",
-         * however adding the "GMT" means we can just use:
-         * <pre>
-         *     dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
-         * </pre>
-         * To read in the time and Get a date which is compatible with our local
-         * time zone.</p>
-         */
-        public string GetTime()
-        {
-            //
-            // standardise the format.
-            //
-            if (time[time.Length - 1] == 'Z')
-            {
-                return time.Substring(0, time.Length - 1) + "GMT+00:00";
-            }
-            else
-            {
-                int signPos = time.Length - 5;
-                char sign = time[signPos];
-                if (sign == '-' || sign == '+')
-                {
-                    return time.Substring(0, signPos)
-                        + "GMT"
-                        + time.Substring(signPos, 3)
-                        + ":"
-                        + time.Substring(signPos + 3);
-                }
-                else
-                {
-                    signPos = time.Length - 3;
-                    sign = time[signPos];
-                    if (sign == '-' || sign == '+')
-                    {
-                        return time.Substring(0, signPos)
-                            + "GMT"
-                            + time.Substring(signPos)
-                            + ":00";
-                    }
-                }
-            }
-
-            return time + CalculateGmtOffset();
-        }
-
-        private string CalculateGmtOffset()
-        {
-            char sign = '+';
-            DateTime time = ToDateTime();
-
-            TimeSpan offset = TimeZoneInfo.Local.GetUtcOffset(time);
-            if (offset.CompareTo(TimeSpan.Zero) < 0)
-            {
-                sign = '-';
-                offset = offset.Duration();
-            }
-            int hours = offset.Hours;
-            int minutes = offset.Minutes;
-
-            return "GMT" + sign + Convert(hours) + ":" + Convert(minutes);
-        }
-
-        private static string Convert(
-            int time)
-        {
-            if (time < 10)
-            {
-                return "0" + time;
-            }
-
-            return time.ToString();
-        }
-
-        public DateTime ToDateTime()
-        {
-            string formatStr;
-            string d = time;
-            bool makeUniversal = false;
-
-            if (Platform.EndsWith(d, "Z"))
-            {
-                if (HasFractionalSeconds)
-                {
-                    int fCount = d.Length - d.IndexOf('.') - 2;
-                    formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"\Z";
-                }
-                else
-                {
-                    formatStr = @"yyyyMMddHHmmss\Z";
-                }
-            }
-            else if (time.IndexOf('-') > 0 || time.IndexOf('+') > 0)
-            {
-                d = GetTime();
-                makeUniversal = true;
-
-                if (HasFractionalSeconds)
-                {
-                    int fCount = Platform.IndexOf(d, "GMT") - 1 - d.IndexOf('.');
-                    formatStr = @"yyyyMMddHHmmss." + FString(fCount) + @"'GMT'zzz";
-                }
-                else
-                {
-                    formatStr = @"yyyyMMddHHmmss'GMT'zzz";
-                }
-            }
-            else
-            {
-                if (HasFractionalSeconds)
-                {
-                    int fCount = d.Length - 1 - d.IndexOf('.');
-                    formatStr = @"yyyyMMddHHmmss." + FString(fCount);
-                }
-                else
-                {
-                    formatStr = @"yyyyMMddHHmmss";
-                }
-
-                // TODO?
-//				dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID()));
-            }
-
-            return ParseDateString(d, formatStr, makeUniversal);
-        }
-
-        private string FString(
-            int count)
-        {
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < count; ++i)
-            {
-                sb.Append('f');
-            }
-            return sb.ToString();
         }
 
-        private DateTime ParseDateString(string	s, string format, bool makeUniversal)
+        public DerGeneralizedTime(string time)
+            : base(time)
         {
-            /*
-             * NOTE: DateTime.Kind and DateTimeStyles.AssumeUniversal not available in .NET 1.1
-             */
-            DateTimeStyles style = DateTimeStyles.None;
-            if (Platform.EndsWith(format, "Z"))
-            {
-                try
-                {
-                    style = (DateTimeStyles)Enums.GetEnumValue(typeof(DateTimeStyles), "AssumeUniversal");
-                }
-                catch (Exception)
-                {
-                }
-
-                style |= DateTimeStyles.AdjustToUniversal;
-            }
-
-            DateTime dt = DateTime.ParseExact(s, format, DateTimeFormatInfo.InvariantInfo, style);
-
-            return makeUniversal ? dt.ToUniversalTime() : dt;
-        }
-
-        private bool HasFractionalSeconds
-        {
-            get { return time.IndexOf('.') == 14; }
-        }
-
-        private byte[] GetOctets()
-        {
-            return Strings.ToAsciiByteArray(time);
         }
 
         internal override IAsn1Encoding GetEncoding(int encoding)
         {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetOctets());
+            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.GeneralizedTime, GetDerTime());
         }
 
         internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
         {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
-        }
-
-        protected override bool Asn1Equals(Asn1Object asn1Object)
-        {
-            DerGeneralizedTime that = asn1Object as DerGeneralizedTime;
-            return null != that
-                && this.time.Equals(that.time);
-        }
-
-        protected override int Asn1GetHashCode()
-        {
-            return time.GetHashCode();
-        }
-
-        internal static DerGeneralizedTime CreatePrimitive(byte[] contents)
-        {
-            return new DerGeneralizedTime(contents);
+            return new PrimitiveEncoding(tagClass, tagNo, GetDerTime());
         }
     }
 }
diff --git a/crypto/src/asn1/DerOctetString.cs b/crypto/src/asn1/DerOctetString.cs
index d9913f065..ea13765ec 100644
--- a/crypto/src/asn1/DerOctetString.cs
+++ b/crypto/src/asn1/DerOctetString.cs
@@ -37,5 +37,22 @@ namespace Org.BouncyCastle.Asn1
             asn1Out.WriteDL(len);
             asn1Out.Write(buf, off, len);
         }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static void Encode(Asn1OutputStream asn1Out, ReadOnlySpan<byte> buf)
+        {
+            asn1Out.WriteIdentifier(Asn1Tags.Universal, Asn1Tags.OctetString);
+            asn1Out.WriteDL(buf.Length);
+            asn1Out.Write(buf);
+        }
+
+        internal static void Encode(Asn1OutputStream asn1Out, ReadOnlySpan<byte> buf1, ReadOnlySpan<byte> buf2)
+        {
+            asn1Out.WriteIdentifier(Asn1Tags.Universal, Asn1Tags.OctetString);
+            asn1Out.WriteDL(buf1.Length + buf2.Length);
+            asn1Out.Write(buf1);
+            asn1Out.Write(buf2);
+        }
+#endif
     }
 }
diff --git a/crypto/src/asn1/DerUTCTime.cs b/crypto/src/asn1/DerUTCTime.cs
index 7f7756d49..089285367 100644
--- a/crypto/src/asn1/DerUTCTime.cs
+++ b/crypto/src/asn1/DerUTCTime.cs
@@ -1,274 +1,25 @@
 using System;
-using System.Globalization;
-using System.IO;
-
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1
 {
-    /**
-     * UTC time object.
-     */
     public class DerUtcTime
-        : Asn1Object
+        : Asn1UtcTime
     {
-        internal class Meta : Asn1UniversalType
-        {
-            internal static readonly Asn1UniversalType Instance = new Meta();
-
-            private Meta() : base(typeof(DerUtcTime), Asn1Tags.UtcTime) {}
-
-            internal override Asn1Object FromImplicitPrimitive(DerOctetString octetString)
-            {
-                return CreatePrimitive(octetString.GetOctets());
-            }
-        }
-
-		/**
-         * return a UTC Time from the passed in object.
-         *
-         * @exception ArgumentException if the object cannot be converted.
-         */
-        public static DerUtcTime GetInstance(object obj)
-        {
-            if (obj == null || obj is DerUtcTime)
-            {
-                return (DerUtcTime)obj;
-            }
-            else if (obj is IAsn1Convertible)
-            {
-                Asn1Object asn1Object = ((IAsn1Convertible)obj).ToAsn1Object();
-                if (asn1Object is DerUtcTime)
-                    return (DerUtcTime)asn1Object;
-            }
-            else if (obj is byte[])
-            {
-                try
-                {
-                    return (DerUtcTime)Meta.Instance.FromByteArray((byte[])obj);
-                }
-                catch (IOException e)
-                {
-                    throw new ArgumentException("failed to construct UTC time from byte[]: " + e.Message);
-                }
-            }
-
-            throw new ArgumentException("illegal object in GetInstance: " + Platform.GetTypeName(obj));
-        }
-
-        /**
-         * return a UTC Time from a tagged object.
-         *
-         * @param taggedObject the tagged object holding the object we want
-         * @param declaredExplicit true if the object is meant to be explicitly tagged false otherwise.
-         * @exception ArgumentException if the tagged object cannot be converted.
-         */
-        public static DerUtcTime GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
-        {
-            return (DerUtcTime)Meta.Instance.GetContextInstance(taggedObject, declaredExplicit);
-        }
-
-        private readonly string time;
-
-        /**
-         * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were
-         * never encoded. When you're creating one of these objects from scratch, that's
-         * what you want to use, otherwise we'll try to deal with whatever Gets read from
-         * the input stream... (this is why the input format is different from the GetTime()
-         * method output).
-         * <p>
-         * @param time the time string.</p>
-         */
         public DerUtcTime(string time)
+			: base(time)
         {
-			if (time == null)
-				throw new ArgumentNullException("time");
-
-			this.time = time;
-
-			try
-			{
-				ToDateTime();
-			}
-			catch (FormatException e)
-			{
-				throw new ArgumentException("invalid date string: " + e.Message);
-			}
         }
 
-		/**
-         * base constructor from a DateTime object
-         */
         public DerUtcTime(DateTime time)
+			: base(time)
         {
-            this.time = time.ToUniversalTime().ToString("yyMMddHHmmss", CultureInfo.InvariantCulture) + "Z";
         }
 
 		internal DerUtcTime(byte[] contents)
+			: base(contents)
         {
-            //
-            // explicitly convert to characters
-            //
-            this.time = Strings.FromAsciiByteArray(contents);
-        }
-
-//		public DateTime ToDateTime()
-//		{
-//			string tm = this.AdjustedTimeString;
-//
-//			return new DateTime(
-//				Int16.Parse(tm.Substring(0, 4)),
-//				Int16.Parse(tm.Substring(4, 2)),
-//				Int16.Parse(tm.Substring(6, 2)),
-//				Int16.Parse(tm.Substring(8, 2)),
-//				Int16.Parse(tm.Substring(10, 2)),
-//				Int16.Parse(tm.Substring(12, 2)));
-//		}
-
-		/**
-		 * return the time as a date based on whatever a 2 digit year will return. For
-		 * standardised processing use ToAdjustedDateTime().
-		 *
-		 * @return the resulting date
-		 * @exception ParseException if the date string cannot be parsed.
-		 */
-		public DateTime ToDateTime()
-		{
-			return ParseDateString(TimeString, @"yyMMddHHmmss'GMT'zzz");
-		}
-
-		/**
-		* return the time as an adjusted date
-		* in the range of 1950 - 2049.
-		*
-		* @return a date in the range of 1950 to 2049.
-		* @exception ParseException if the date string cannot be parsed.
-		*/
-		public DateTime ToAdjustedDateTime()
-		{
-			return ParseDateString(AdjustedTimeString, @"yyyyMMddHHmmss'GMT'zzz");
-		}
-
-		private DateTime ParseDateString(string dateStr, string formatStr)
-		{
-			DateTime dt = DateTime.ParseExact(
-				dateStr,
-				formatStr,
-				DateTimeFormatInfo.InvariantInfo);
-
-			return dt.ToUniversalTime();
-		}
-
-		/**
-         * return the time - always in the form of
-         *  YYMMDDhhmmssGMT(+hh:mm|-hh:mm).
-         * <p>
-         * Normally in a certificate we would expect "Z" rather than "GMT",
-         * however adding the "GMT" means we can just use:
-         * <pre>
-         *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
-         * </pre>
-         * To read in the time and Get a date which is compatible with our local
-         * time zone.</p>
-         * <p>
-         * <b>Note:</b> In some cases, due to the local date processing, this
-         * may lead to unexpected results. If you want to stick the normal
-         * convention of 1950 to 2049 use the GetAdjustedTime() method.</p>
-         */
-        public string TimeString
-        {
-			get
-			{
-				//
-				// standardise the format.
-				//
-				if (time.IndexOf('-') < 0 && time.IndexOf('+') < 0)
-				{
-					if (time.Length == 11)
-					{
-						return time.Substring(0, 10) + "00GMT+00:00";
-					}
-					else
-					{
-						return time.Substring(0, 12) + "GMT+00:00";
-					}
-				}
-				else
-				{
-					int index = time.IndexOf('-');
-					if (index < 0)
-					{
-						index = time.IndexOf('+');
-					}
-					string d = time;
-
-					if (index == time.Length - 3)
-					{
-						d += "00";
-					}
-
-					if (index == 10)
-					{
-						return d.Substring(0, 10) + "00GMT" + d.Substring(10, 3) + ":" + d.Substring(13, 2);
-					}
-					else
-					{
-						return d.Substring(0, 12) + "GMT" + d.Substring(12, 3) + ":" +  d.Substring(15, 2);
-					}
-				}
-			}
-        }
-
-		/// <summary>
-		/// Return a time string as an adjusted date with a 4 digit year.
-		/// This goes in the range of 1950 - 2049.
-		/// </summary>
-		public string AdjustedTimeString
-		{
-			get
-			{
-				string d = TimeString;
-				string c = d[0] < '5' ? "20" : "19";
-
-				return c + d;
-			}
-		}
-
-        private byte[] GetOctets()
-        {
-            return Strings.ToAsciiByteArray(time);
-        }
-
-        internal override IAsn1Encoding GetEncoding(int encoding)
-        {
-            return new PrimitiveEncoding(Asn1Tags.Universal, Asn1Tags.UtcTime, GetOctets());
-        }
-
-        internal override IAsn1Encoding GetEncodingImplicit(int encoding, int tagClass, int tagNo)
-        {
-            return new PrimitiveEncoding(tagClass, tagNo, GetOctets());
-        }
-
-        protected override bool Asn1Equals(Asn1Object asn1Object)
-		{
-			DerUtcTime that = asn1Object as DerUtcTime;
-            return null != that
-                && this.time.Equals(that.time);
         }
 
-		protected override int Asn1GetHashCode()
-		{
-            return time.GetHashCode();
-        }
-
-		public override string ToString()
-		{
-			return time;
-		}
-
-        internal static DerUtcTime CreatePrimitive(byte[] contents)
-        {
-            return new DerUtcTime(contents);
-        }
+        // TODO: create proper DER encoding.
     }
 }
diff --git a/crypto/src/asn1/IndefiniteLengthInputStream.cs b/crypto/src/asn1/IndefiniteLengthInputStream.cs
index 1c8bd9a15..e192e9e8b 100644
--- a/crypto/src/asn1/IndefiniteLengthInputStream.cs
+++ b/crypto/src/asn1/IndefiniteLengthInputStream.cs
@@ -57,7 +57,28 @@ namespace Org.BouncyCastle.Asn1
 			return numRead + 1;
 		}
 
-		public override int ReadByte()
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public override int Read(Span<byte> buffer)
+        {
+            // Only use this optimisation if we aren't checking for 00
+            if (_eofOn00 || buffer.Length <= 1)
+                return base.Read(buffer);
+
+            if (_lookAhead < 0)
+                return 0;
+
+            int numRead = _in.Read(buffer[1..]);
+            if (numRead <= 0)
+                throw new EndOfStreamException();
+
+            buffer[0] = (byte)_lookAhead;
+            _lookAhead = RequireByte();
+
+            return numRead + 1;
+        }
+#endif
+
+        public override int ReadByte()
 		{
             if (_eofOn00 && _lookAhead <= 0)
             {
diff --git a/crypto/src/asn1/bc/BCObjectIdentifiers.cs b/crypto/src/asn1/bc/BCObjectIdentifiers.cs
index a649e0c73..d526980e5 100644
--- a/crypto/src/asn1/bc/BCObjectIdentifiers.cs
+++ b/crypto/src/asn1/bc/BCObjectIdentifiers.cs
@@ -116,6 +116,27 @@ namespace Org.BouncyCastle.Asn1.BC
         public static readonly DerObjectIdentifier picnicl1full = picnic.Branch("10");
         public static readonly DerObjectIdentifier picnicl3full = picnic.Branch("11");
         public static readonly DerObjectIdentifier picnicl5full = picnic.Branch("12");
+        
+        /*
+         * Falcon
+         */
+        public static readonly DerObjectIdentifier falcon = bc_sig.Branch("7");
+
+        public static readonly DerObjectIdentifier falcon_512 = new DerObjectIdentifier("1.3.9999.3.1");  // falcon.branch("1");
+        public static readonly DerObjectIdentifier falcon_1024 =  new DerObjectIdentifier("1.3.9999.3.4"); // falcon.branch("2");
+
+        /*
+         * Dilithium
+         */
+        public static readonly DerObjectIdentifier dilithium = bc_sig.Branch("8");
+
+        // OpenSSL OIDs
+        public static readonly DerObjectIdentifier dilithium2 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.7.4.4"); // dilithium.branch("1");
+        public static readonly DerObjectIdentifier dilithium3 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.7.6.5"); // dilithium.branch("2");
+        public static readonly DerObjectIdentifier dilithium5 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.7.8.7"); // dilithium.branch("3");
+        public static readonly DerObjectIdentifier dilithium2_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.11.4.4"); // dilithium.branch("4");
+        public static readonly DerObjectIdentifier dilithium3_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.11.6.5"); // dilithium.branch("5");
+        public static readonly DerObjectIdentifier dilithium5_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.11.8.7"); // dilithium.branch("6");
 
 
         /**
@@ -171,7 +192,16 @@ namespace Org.BouncyCastle.Asn1.BC
         public static readonly DerObjectIdentifier lightsaberkem256r3 = pqc_kem_saber.Branch("7");
         public static readonly DerObjectIdentifier saberkem256r3 = pqc_kem_saber.Branch("8");
         public static readonly DerObjectIdentifier firesaberkem256r3 = pqc_kem_saber.Branch("9");
-        
+        public static readonly DerObjectIdentifier ulightsaberkemr3 = pqc_kem_saber.Branch("10");
+        public static readonly DerObjectIdentifier usaberkemr3 = pqc_kem_saber.Branch("11");
+        public static readonly DerObjectIdentifier ufiresaberkemr3 = pqc_kem_saber.Branch("12");
+        public static readonly DerObjectIdentifier lightsaberkem90sr3 = pqc_kem_saber.Branch("13");
+        public static readonly DerObjectIdentifier saberkem90sr3 = pqc_kem_saber.Branch("14");
+        public static readonly DerObjectIdentifier firesaberkem90sr3 = pqc_kem_saber.Branch("15");
+        public static readonly DerObjectIdentifier ulightsaberkem90sr3 = pqc_kem_saber.Branch("16");
+        public static readonly DerObjectIdentifier usaberkem90sr3 = pqc_kem_saber.Branch("17");
+        public static readonly DerObjectIdentifier ufiresaberkem90sr3 = pqc_kem_saber.Branch("18");
+
         /**
          * SIKE
          */
@@ -184,5 +214,35 @@ namespace Org.BouncyCastle.Asn1.BC
         public static readonly DerObjectIdentifier sikep503_compressed = pqc_kem_sike.Branch("6");
         public static readonly DerObjectIdentifier sikep610_compressed = pqc_kem_sike.Branch("7");
         public static readonly DerObjectIdentifier sikep751_compressed = pqc_kem_sike.Branch("8");
-	}
+        
+        /**
+         * Kyber
+         */
+        public static readonly DerObjectIdentifier pqc_kem_kyber = bc_kem.Branch("6");
+
+        public static readonly DerObjectIdentifier kyber512 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.2.2"); // pqc_kem_kyber.Branch("1");
+        public static readonly DerObjectIdentifier kyber768 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.3.3"); // pqc_kem_kyber.Branch("2");
+        public static readonly DerObjectIdentifier kyber1024 = new DerObjectIdentifier("1.3.6.1.4.1.2.267.8.4.4"); // pqc_kem_kyber.Branch("3");
+        public static readonly DerObjectIdentifier kyber512_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.2.2"); // pqc_kem_kyber.Branch("4");
+        public static readonly DerObjectIdentifier kyber768_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.3.3"); // pqc_kem_kyber.Branch("5");
+        public static readonly DerObjectIdentifier kyber1024_aes = new DerObjectIdentifier("1.3.6.1.4.1.2.267.10.4.4"); // pqc_kem_kyber.Branch("6");
+
+        /**
+         * BIKE
+         */
+        public static readonly DerObjectIdentifier pqc_kem_bike = bc_kem.Branch("8");
+
+        public static readonly DerObjectIdentifier bike128 = pqc_kem_bike.Branch("1");
+        public static readonly DerObjectIdentifier bike192 = pqc_kem_bike.Branch("2");
+        public static readonly DerObjectIdentifier bike256 = pqc_kem_bike.Branch("3");
+
+        /**
+         * HQC
+         */
+        public static readonly DerObjectIdentifier pqc_kem_hqc = bc_kem.Branch("9");
+
+        public static readonly DerObjectIdentifier hqc128 = pqc_kem_hqc.Branch("1");
+        public static readonly DerObjectIdentifier hqc192 = pqc_kem_hqc.Branch("2");
+        public static readonly DerObjectIdentifier hqc256 = pqc_kem_hqc.Branch("3");
+    }
 }
diff --git a/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs b/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs
index b74bac87a..a3ec5e4df 100644
--- a/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs
+++ b/crypto/src/asn1/cmp/CAKeyUpdAnnContent.cs
@@ -7,42 +7,33 @@ namespace Org.BouncyCastle.Asn1.Cmp
 	public class CAKeyUpdAnnContent
 		: Asn1Encodable
 	{
-		private readonly CmpCertificate oldWithNew;
-		private readonly CmpCertificate newWithOld;
-		private readonly CmpCertificate newWithNew;
+        public static CAKeyUpdAnnContent GetInstance(object obj)
+        {
+            if (obj is CAKeyUpdAnnContent content)
+                return content;
 
-		private CAKeyUpdAnnContent(Asn1Sequence seq)
-		{
-			oldWithNew = CmpCertificate.GetInstance(seq[0]);
-			newWithOld = CmpCertificate.GetInstance(seq[1]);
-			newWithNew = CmpCertificate.GetInstance(seq[2]);
-		}
+            if (obj is Asn1Sequence seq)
+                return new CAKeyUpdAnnContent(seq);
 
-		public static CAKeyUpdAnnContent GetInstance(object obj)
-		{
-			if (obj is CAKeyUpdAnnContent)
-				return (CAKeyUpdAnnContent)obj;
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
+        }
 
-			if (obj is Asn1Sequence)
-				return new CAKeyUpdAnnContent((Asn1Sequence)obj);
+        private readonly CmpCertificate m_oldWithNew;
+		private readonly CmpCertificate m_newWithOld;
+		private readonly CmpCertificate m_newWithNew;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
-
-		public virtual CmpCertificate OldWithNew
-		{
-			get { return oldWithNew; }
-		}
-		
-		public virtual CmpCertificate NewWithOld
+		private CAKeyUpdAnnContent(Asn1Sequence seq)
 		{
-			get { return newWithOld; }
+			m_oldWithNew = CmpCertificate.GetInstance(seq[0]);
+			m_newWithOld = CmpCertificate.GetInstance(seq[1]);
+			m_newWithNew = CmpCertificate.GetInstance(seq[2]);
 		}
 
-		public virtual CmpCertificate NewWithNew
-		{
-			get { return newWithNew; }
-		}
+		public virtual CmpCertificate OldWithNew => m_oldWithNew;
+
+		public virtual CmpCertificate NewWithOld => m_newWithOld;
+
+		public virtual CmpCertificate NewWithNew => m_newWithNew;
 
 		/**
 		 * <pre>
@@ -56,7 +47,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return new DerSequence(oldWithNew, newWithOld, newWithNew);
+			return new DerSequence(m_oldWithNew, m_newWithOld, m_newWithNew);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/CertAnnContent.cs b/crypto/src/asn1/cmp/CertAnnContent.cs
new file mode 100644
index 000000000..30b1fad2f
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertAnnContent.cs
@@ -0,0 +1,72 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     *  CertAnnContent ::= CMPCertificate
+     */
+    public class CertAnnContent
+        : CmpCertificate
+    {
+        public static new CertAnnContent GetInstance(object obj)
+        {
+            // TODO[cmp]
+            if (obj == null)
+                return null;
+
+            if (obj is CertAnnContent content)
+                return content;
+
+            if (obj is CmpCertificate cmpCertificate)
+                return GetInstance(cmpCertificate.GetEncoded());
+
+            if (obj is byte[] bs)
+            {
+                try
+                {
+                    obj = Asn1Object.FromByteArray(bs);
+                }
+                catch (IOException)
+                {
+                    throw new ArgumentException("Invalid encoding in CertAnnContent");
+                }
+            }
+
+            if (obj is Asn1Sequence)
+                return new CertAnnContent(X509CertificateStructure.GetInstance(obj));
+
+            // TODO[cmp]
+            if (obj is Asn1TaggedObject taggedObject)
+                return new CertAnnContent(taggedObject.TagNo, taggedObject.GetObject());
+
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
+        }
+
+        public static new CertAnnContent GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            // TODO[cmp]
+            if (taggedObject == null)
+                return null;
+
+            if (!declaredExplicit)
+                throw new ArgumentException("tag must be explicit");
+
+            // TODO[cmp]
+            return GetInstance(taggedObject.GetObject());
+        }
+
+        public CertAnnContent(int type, Asn1Object otherCert)
+            : base(type, otherCert)
+        {
+        }
+
+        public CertAnnContent(X509CertificateStructure x509v3PKCert)
+            : base(x509v3PKCert)
+        {
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/CertConfirmContent.cs b/crypto/src/asn1/cmp/CertConfirmContent.cs
index 370a9e7d6..8e75dfbd0 100644
--- a/crypto/src/asn1/cmp/CertConfirmContent.cs
+++ b/crypto/src/asn1/cmp/CertConfirmContent.cs
@@ -7,32 +7,27 @@ namespace Org.BouncyCastle.Asn1.Cmp
 	public class CertConfirmContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
-
-		private CertConfirmContent(Asn1Sequence seq)
-		{
-			content = seq;
-		}
-
 		public static CertConfirmContent GetInstance(object obj)
 		{
-			if (obj is CertConfirmContent)
-				return (CertConfirmContent)obj;
+			if (obj is CertConfirmContent content)
+				return content;
 
-			if (obj is Asn1Sequence)
-				return new CertConfirmContent((Asn1Sequence)obj);
+			if (obj is Asn1Sequence seq)
+				return new CertConfirmContent(seq);
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
 		}
 
-		public virtual CertStatus[] ToCertStatusArray()
+        private readonly Asn1Sequence m_content;
+
+        private CertConfirmContent(Asn1Sequence seq)
+        {
+            m_content = seq;
+        }
+
+        public virtual CertStatus[] ToCertStatusArray()
 		{
-			CertStatus[] result = new CertStatus[content.Count];
-			for (int i = 0; i != result.Length; i++)
-			{
-				result[i] = CertStatus.GetInstance(content[i]);
-			}
-			return result;
+			return m_content.MapElements(CertStatus.GetInstance);
 		}
 
 		/**
@@ -43,7 +38,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return content;
+			return m_content;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/CertOrEncCert.cs b/crypto/src/asn1/cmp/CertOrEncCert.cs
index eb200e1e8..e517b66ce 100644
--- a/crypto/src/asn1/cmp/CertOrEncCert.cs
+++ b/crypto/src/asn1/cmp/CertOrEncCert.cs
@@ -8,79 +8,79 @@ namespace Org.BouncyCastle.Asn1.Cmp
 	public class CertOrEncCert
 		: Asn1Encodable, IAsn1Choice
 	{
-		private readonly CmpCertificate certificate;
-		private readonly EncryptedValue encryptedCert;
+        public static CertOrEncCert GetInstance(object obj)
+        {
+            if (obj is CertOrEncCert certOrEncCert)
+                return certOrEncCert;
 
-		private CertOrEncCert(Asn1TaggedObject tagged)
+            if (obj is Asn1TaggedObject taggedObject)
+                return new CertOrEncCert(taggedObject);
+
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
+        }
+
+        private readonly CmpCertificate m_certificate;
+		private readonly EncryptedKey m_encryptedCert;
+
+		private CertOrEncCert(Asn1TaggedObject taggedObject)
 		{
-			if (tagged.TagNo == 0)
+			if (taggedObject.TagNo == 0)
 			{
-				certificate = CmpCertificate.GetInstance(tagged.GetObject());
+				m_certificate = CmpCertificate.GetInstance(taggedObject.GetObject());
 			}
-			else if (tagged.TagNo == 1)
+			else if (taggedObject.TagNo == 1)
 			{
-				encryptedCert = EncryptedValue.GetInstance(tagged.GetObject());
+                m_encryptedCert = EncryptedKey.GetInstance(taggedObject.GetObject());
 			}
 			else
 			{
-				throw new ArgumentException("unknown tag: " + tagged.TagNo, "tagged");
-			}
-		}
-		
-		public static CertOrEncCert GetInstance(object obj)
-		{
-			if (obj is CertOrEncCert)
-				return (CertOrEncCert)obj;
-
-			if (obj is Asn1TaggedObject)
-				return new CertOrEncCert((Asn1TaggedObject)obj);
-
-			throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
+				throw new ArgumentException("unknown tag: " + taggedObject.TagNo, nameof(taggedObject));
+            }
+        }
 
 		public CertOrEncCert(CmpCertificate certificate)
 		{
 			if (certificate == null)
-				throw new ArgumentNullException("certificate");
+				throw new ArgumentNullException(nameof(certificate));
 
-			this.certificate = certificate;
+			m_certificate = certificate;
 		}
 
-		public CertOrEncCert(EncryptedValue encryptedCert)
+		public CertOrEncCert(EncryptedValue encryptedValue)
 		{
-			if (encryptedCert == null)
-				throw new ArgumentNullException("encryptedCert");
+			if (encryptedValue == null)
+				throw new ArgumentNullException(nameof(encryptedValue));
 
-			this.encryptedCert = encryptedCert;
+			m_encryptedCert = new EncryptedKey(encryptedValue);
 		}
 
-		public virtual CmpCertificate Certificate
-		{
-			get { return certificate; }
-		}
+        public CertOrEncCert(EncryptedKey encryptedKey)
+        {
+            if (encryptedKey == null)
+                throw new ArgumentNullException(nameof(encryptedKey));
 
-		public virtual EncryptedValue EncryptedCert
-		{
-			get { return encryptedCert; }
-		}
+            m_encryptedCert = encryptedKey;
+        }
 
-		/**
+		public virtual CmpCertificate Certificate => m_certificate;
+
+		public virtual EncryptedKey EncryptedCert => m_encryptedCert;
+
+        /**
 		 * <pre>
 		 * CertOrEncCert ::= CHOICE {
 		 *                      certificate     [0] CMPCertificate,
-		 *                      encryptedCert   [1] EncryptedValue
+		 *                      encryptedCert   [1] EncryptedKey
 		 *           }
 		 * </pre>
 		 * @return a basic ASN.1 object representation.
 		 */
-		public override Asn1Object ToAsn1Object()
+        public override Asn1Object ToAsn1Object()
 		{
-			if (certificate != null)
-			{
-				return new DerTaggedObject(true, 0, certificate);
-			}
+			if (m_certificate != null)
+				return new DerTaggedObject(true, 0, m_certificate);
 
-			return new DerTaggedObject(true, 1, encryptedCert);
+			return new DerTaggedObject(true, 1, m_encryptedCert);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/CertRepMessage.cs b/crypto/src/asn1/cmp/CertRepMessage.cs
index d24dd963b..696cfde47 100644
--- a/crypto/src/asn1/cmp/CertRepMessage.cs
+++ b/crypto/src/asn1/cmp/CertRepMessage.cs
@@ -7,8 +7,19 @@ namespace Org.BouncyCastle.Asn1.Cmp
 	public class CertRepMessage
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence caPubs;
-		private readonly Asn1Sequence response;
+        public static CertRepMessage GetInstance(object obj)
+        {
+            if (obj is CertRepMessage certRepMessage)
+                return certRepMessage;
+
+            if (obj != null)
+				return new CertRepMessage(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly Asn1Sequence m_caPubs;
+		private readonly Asn1Sequence m_response;
 		
 		private CertRepMessage(Asn1Sequence seq)
 		{
@@ -16,57 +27,33 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 			if (seq.Count > 1)
 			{
-				caPubs = Asn1Sequence.GetInstance((Asn1TaggedObject)seq[index++], true);
+				m_caPubs = Asn1Sequence.GetInstance((Asn1TaggedObject)seq[index++], true);
 			}
 
-			response = Asn1Sequence.GetInstance(seq[index]);
-		}
-
-		public static CertRepMessage GetInstance(object obj)
-		{
-			if (obj is CertRepMessage)
-				return (CertRepMessage)obj;
-
-			if (obj is Asn1Sequence)
-				return new CertRepMessage((Asn1Sequence)obj);
-
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+			m_response = Asn1Sequence.GetInstance(seq[index]);
 		}
 
 		public CertRepMessage(CmpCertificate[] caPubs, CertResponse[] response)
 		{
 			if (response == null)
-				throw new ArgumentNullException("response");
+				throw new ArgumentNullException(nameof(response));
 
 			if (caPubs != null)
 			{
-				this.caPubs = new DerSequence(caPubs);
+				m_caPubs = new DerSequence(caPubs);
 			}
 
-			this.response = new DerSequence(response);
+			m_response = new DerSequence(response);
 		}
 
 		public virtual CmpCertificate[] GetCAPubs()
 		{
-			if (caPubs == null)
-				return null;
-
-			CmpCertificate[] results = new CmpCertificate[caPubs.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = CmpCertificate.GetInstance(caPubs[i]);
-			}
-			return results;
+			return m_caPubs == null ? null : m_caPubs.MapElements(CmpCertificate.GetInstance);
 		}
 
 		public virtual CertResponse[] GetResponse()
 		{
-			CertResponse[] results = new CertResponse[response.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = CertResponse.GetInstance(response[i]);
-			}
-			return results;
+            return m_response.MapElements(CertResponse.GetInstance);
 		}
 
 		/**
@@ -81,9 +68,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector();
-            v.AddOptionalTagged(true, 1, caPubs);
-			v.Add(response);
+			Asn1EncodableVector v = new Asn1EncodableVector(2);
+            v.AddOptionalTagged(true, 1, m_caPubs);
+			v.Add(m_response);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/CertReqTemplateContent.cs b/crypto/src/asn1/cmp/CertReqTemplateContent.cs
new file mode 100644
index 000000000..c25c71ad1
--- /dev/null
+++ b/crypto/src/asn1/cmp/CertReqTemplateContent.cs
@@ -0,0 +1,67 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * GenMsg:    {id-it 19}, &lt; absent &gt;
+     * GenRep:    {id-it 19}, CertReqTemplateContent | &lt; absent &gt;
+     * <p>
+     * CertReqTemplateValue  ::= CertReqTemplateContent
+     * </p><p>
+     * CertReqTemplateContent ::= SEQUENCE {
+     * certTemplate           CertTemplate,
+     * keySpec                Controls OPTIONAL }
+     * </p><p>
+     * Controls  ::= SEQUENCE SIZE (1..MAX) OF AttributeTypeAndValue
+     * </p>
+     */
+    public class CertReqTemplateContent
+        : Asn1Encodable
+    {
+        public static CertReqTemplateContent GetInstance(object obj)
+        {
+            if (obj is CertReqTemplateContent certReqTemplateContent)
+                return certReqTemplateContent;
+
+            if (obj != null)
+                return new CertReqTemplateContent(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly CertTemplate m_certTemplate;
+        private readonly Asn1Sequence m_keySpec;
+
+        private CertReqTemplateContent(Asn1Sequence seq)
+        {
+            if (seq.Count != 1 && seq.Count != 2)
+                throw new ArgumentException("expected sequence size of 1 or 2");
+
+            m_certTemplate = CertTemplate.GetInstance(seq[0]);
+
+            if (seq.Count > 1)
+            {
+                m_keySpec = Asn1Sequence.GetInstance(seq[1]);
+            }
+        }
+
+        public CertReqTemplateContent(CertTemplate certTemplate, Asn1Sequence keySpec)
+        {
+            m_certTemplate = certTemplate;
+            m_keySpec = keySpec;
+        }
+
+        public virtual CertTemplate CertTemplate => m_certTemplate;
+
+        public virtual Asn1Sequence KeySpec => m_keySpec;
+
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(m_certTemplate);
+            v.AddOptional(m_keySpec);
+            return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/CertResponse.cs b/crypto/src/asn1/cmp/CertResponse.cs
index 843fd9299..72a44c93e 100644
--- a/crypto/src/asn1/cmp/CertResponse.cs
+++ b/crypto/src/asn1/cmp/CertResponse.cs
@@ -1,21 +1,30 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class CertResponse
 		: Asn1Encodable
 	{
-		private readonly DerInteger certReqId;
-		private readonly PkiStatusInfo status;
-		private readonly CertifiedKeyPair certifiedKeyPair;
-		private readonly Asn1OctetString rspInfo;
+        public static CertResponse GetInstance(object obj)
+        {
+			if (obj is CertResponse certResponse)
+				return certResponse;
+
+			if (obj != null)
+				return new CertResponse(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly DerInteger m_certReqId;
+		private readonly PkiStatusInfo m_status;
+		private readonly CertifiedKeyPair m_certifiedKeyPair;
+		private readonly Asn1OctetString m_rspInfo;
 
 		private CertResponse(Asn1Sequence seq)
 		{
-			certReqId = DerInteger.GetInstance(seq[0]);
-			status = PkiStatusInfo.GetInstance(seq[1]);
+			m_certReqId = DerInteger.GetInstance(seq[0]);
+			m_status = PkiStatusInfo.GetInstance(seq[1]);
 
 			if (seq.Count >= 3)
 			{
@@ -24,71 +33,46 @@ namespace Org.BouncyCastle.Asn1.Cmp
 					Asn1Encodable o = seq[2];
 					if (o is Asn1OctetString)
 					{
-						rspInfo = Asn1OctetString.GetInstance(o);
+						m_rspInfo = Asn1OctetString.GetInstance(o);
 					}
 					else
 					{
-						certifiedKeyPair = CertifiedKeyPair.GetInstance(o);
+						m_certifiedKeyPair = CertifiedKeyPair.GetInstance(o);
 					}
 				}
 				else
 				{
-					certifiedKeyPair = CertifiedKeyPair.GetInstance(seq[2]);
-					rspInfo = Asn1OctetString.GetInstance(seq[3]);
+					m_certifiedKeyPair = CertifiedKeyPair.GetInstance(seq[2]);
+					m_rspInfo = Asn1OctetString.GetInstance(seq[3]);
 				}
 			}
 		}
 
-		public static CertResponse GetInstance(object obj)
-		{
-			if (obj is CertResponse)
-				return (CertResponse)obj;
-
-			if (obj is Asn1Sequence)
-				return new CertResponse((Asn1Sequence)obj);
-
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
-
-		public CertResponse(
-			DerInteger certReqId,
-			PkiStatusInfo status)
+		public CertResponse(DerInteger certReqId, PkiStatusInfo status)
 			: this(certReqId, status, null, null)
 		{
 		}
 
-		public CertResponse(
-			DerInteger			certReqId,
-			PkiStatusInfo		status,
-			CertifiedKeyPair	certifiedKeyPair,
-			Asn1OctetString		rspInfo)
-		{
-			if (certReqId == null)
-				throw new ArgumentNullException("certReqId");
+        public CertResponse(DerInteger certReqId, PkiStatusInfo status, CertifiedKeyPair certifiedKeyPair,
+            Asn1OctetString rspInfo)
+        {
+            if (certReqId == null)
+				throw new ArgumentNullException(nameof(certReqId));
 
 			if (status == null)
-				throw new ArgumentNullException("status");
+				throw new ArgumentNullException(nameof(status));
 
-			this.certReqId = certReqId;
-			this.status = status;
-			this.certifiedKeyPair = certifiedKeyPair;
-			this.rspInfo = rspInfo;
+			m_certReqId = certReqId;
+			m_status = status;
+			m_certifiedKeyPair = certifiedKeyPair;
+			m_rspInfo = rspInfo;
 		}
 
-		public virtual DerInteger CertReqID
-		{
-			get { return certReqId; }
-		}
+		public virtual DerInteger CertReqID => m_certReqId;
 
-		public virtual PkiStatusInfo Status
-		{
-			get { return status; }
-		}
+		public virtual PkiStatusInfo Status => m_status;
 
-		public virtual CertifiedKeyPair CertifiedKeyPair
-		{
-			get { return certifiedKeyPair; }
-		}
+		public virtual CertifiedKeyPair CertifiedKeyPair => m_certifiedKeyPair;
 
 		/**
 		 * <pre>
@@ -108,8 +92,8 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(certReqId, status);
-			v.AddOptional(certifiedKeyPair, rspInfo);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_certReqId, m_status);
+			v.AddOptional(m_certifiedKeyPair, m_rspInfo);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/CertStatus.cs b/crypto/src/asn1/cmp/CertStatus.cs
index d437b57b2..6eb36c6fb 100644
--- a/crypto/src/asn1/cmp/CertStatus.cs
+++ b/crypto/src/asn1/cmp/CertStatus.cs
@@ -1,84 +1,102 @@
 using System;
 
+using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class CertStatus
 		: Asn1Encodable
 	{
-		private readonly Asn1OctetString certHash;
-		private readonly DerInteger certReqId;
-		private readonly PkiStatusInfo statusInfo;
+        public static CertStatus GetInstance(object obj)
+        {
+			if (obj is CertStatus certStatus)
+				return certStatus;
 
-		private CertStatus(Asn1Sequence seq)
+			if (obj != null)
+				return new CertStatus(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly Asn1OctetString m_certHash;
+		private readonly DerInteger m_certReqID;
+		private readonly PkiStatusInfo m_statusInfo;
+        private readonly AlgorithmIdentifier m_hashAlg;
+
+        private CertStatus(Asn1Sequence seq)
 		{
-			certHash = Asn1OctetString.GetInstance(seq[0]);
-			certReqId = DerInteger.GetInstance(seq[1]);
+			m_certHash = Asn1OctetString.GetInstance(seq[0]);
+			m_certReqID = DerInteger.GetInstance(seq[1]);
 
 			if (seq.Count > 2)
 			{
-				statusInfo = PkiStatusInfo.GetInstance(seq[2]);
+				for (int t = 2; t < seq.Count; t++)
+				{
+					Asn1Object p = seq[t].ToAsn1Object();
+					if (p is Asn1Sequence s)
+					{
+						m_statusInfo = PkiStatusInfo.GetInstance(s);
+					}
+					if (p is Asn1TaggedObject dto)
+					{
+						if (dto.TagNo != 0)
+							throw new ArgumentException("unknown tag " + dto.TagNo);
+
+						m_hashAlg = AlgorithmIdentifier.GetInstance(dto, true);
+					}
+				}
 			}
 		}
 
-		public CertStatus(byte[] certHash, BigInteger certReqId)
+		public CertStatus(byte[] certHash, BigInteger certReqID)
 		{
-			this.certHash = new DerOctetString(certHash);
-			this.certReqId = new DerInteger(certReqId);
+			m_certHash = new DerOctetString(certHash);
+			m_certReqID = new DerInteger(certReqID);
 		}
 
-		public CertStatus(byte[] certHash, BigInteger certReqId, PkiStatusInfo statusInfo)
+		public CertStatus(byte[] certHash, BigInteger certReqID, PkiStatusInfo statusInfo)
 		{
-			this.certHash = new DerOctetString(certHash);
-			this.certReqId = new DerInteger(certReqId);
-			this.statusInfo = statusInfo;
+            m_certHash = new DerOctetString(certHash);
+            m_certReqID = new DerInteger(certReqID);
+            m_statusInfo = statusInfo;
 		}
 
-		public static CertStatus GetInstance(object obj)
-		{
-			if (obj is CertStatus)
-				return (CertStatus)obj;
+        public CertStatus(byte[] certHash, BigInteger certReqID, PkiStatusInfo statusInfo, AlgorithmIdentifier hashAlg)
+        {
+            m_certHash = new DerOctetString(certHash);
+            m_certReqID = new DerInteger(certReqID);
+            m_statusInfo = statusInfo;
+            m_hashAlg = hashAlg;
+        }
 
-			if (obj is Asn1Sequence)
-				return new CertStatus((Asn1Sequence)obj);
+        public virtual Asn1OctetString CertHash => m_certHash;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
+		public virtual DerInteger CertReqID => m_certReqID;
 
-		public virtual Asn1OctetString CertHash
-		{
-			get { return certHash; }
-		}
-
-		public virtual DerInteger CertReqID
-		{
-			get { return certReqId; }
-		}
+		public virtual PkiStatusInfo StatusInfo => m_statusInfo;
 
-		public virtual PkiStatusInfo StatusInfo
-		{
-			get { return statusInfo; }
-		}
+		public virtual AlgorithmIdentifier HashAlg => m_hashAlg;
 
-		/**
-		 * <pre>
-		 * CertStatus ::= SEQUENCE {
-		 *                   certHash    OCTET STRING,
-		 *                   -- the hash of the certificate, using the same hash algorithm
-		 *                   -- as is used to create and verify the certificate signature
-		 *                   certReqId   INTEGER,
-		 *                   -- to match this confirmation with the corresponding req/rep
-		 *                   statusInfo  PKIStatusInfo OPTIONAL
-		 * }
-		 * </pre>
-		 * @return a basic ASN.1 object representation.
-		 */
-		public override Asn1Object ToAsn1Object()
+        /**
+         * <pre>
+         *
+         *  CertStatus ::= SEQUENCE {
+         *     certHash    OCTET STRING,
+         *     certReqId   INTEGER,
+         *     statusInfo  PKIStatusInfo OPTIONAL,
+         *     hashAlg [0] AlgorithmIdentifier{DIGEST-ALGORITHM, {...}} OPTIONAL
+         *   }
+         *
+         * </pre>
+         *
+         * @return a basic ASN.1 object representation.
+         */
+        public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(certHash, certReqId);
-			v.AddOptional(statusInfo);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_certHash, m_certReqID);
+			v.AddOptional(m_statusInfo);
+			v.AddOptionalTagged(true, 0, m_hashAlg);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/CertifiedKeyPair.cs b/crypto/src/asn1/cmp/CertifiedKeyPair.cs
index 0b1c5d44d..ec60ce965 100644
--- a/crypto/src/asn1/cmp/CertifiedKeyPair.cs
+++ b/crypto/src/asn1/cmp/CertifiedKeyPair.cs
@@ -1,20 +1,30 @@
 using System;
 
 using Org.BouncyCastle.Asn1.Crmf;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class CertifiedKeyPair
 		: Asn1Encodable
 	{
-		private readonly CertOrEncCert certOrEncCert;
-		private readonly EncryptedValue privateKey;
-		private readonly PkiPublicationInfo publicationInfo;
+        public static CertifiedKeyPair GetInstance(object obj)
+        {
+            if (obj is CertifiedKeyPair certifiedKeyPair)
+                return certifiedKeyPair;
 
-		private CertifiedKeyPair(Asn1Sequence seq)
+            if (obj != null)
+                return new CertifiedKeyPair(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly CertOrEncCert m_certOrEncCert;
+		private readonly EncryptedKey m_privateKey;
+		private readonly PkiPublicationInfo m_publicationInfo;
+
+        private CertifiedKeyPair(Asn1Sequence seq)
 		{
-			certOrEncCert = CertOrEncCert.GetInstance(seq[0]);
+			m_certOrEncCert = CertOrEncCert.GetInstance(seq[0]);
 
 			if (seq.Count >= 2)
 			{
@@ -23,66 +33,48 @@ namespace Org.BouncyCastle.Asn1.Cmp
 					Asn1TaggedObject tagged = Asn1TaggedObject.GetInstance(seq[1]);
 					if (tagged.TagNo == 0)
 					{
-						privateKey = EncryptedValue.GetInstance(tagged.GetObject());
+						m_privateKey = EncryptedKey.GetInstance(tagged.GetObject());
 					}
 					else
 					{
-						publicationInfo = PkiPublicationInfo.GetInstance(tagged.GetObject());
+						m_publicationInfo = PkiPublicationInfo.GetInstance(tagged.GetObject());
 					}
 				}
 				else
 				{
-					privateKey = EncryptedValue.GetInstance(Asn1TaggedObject.GetInstance(seq[1]));
-					publicationInfo = PkiPublicationInfo.GetInstance(Asn1TaggedObject.GetInstance(seq[2]));
+                    m_privateKey = EncryptedKey.GetInstance(Asn1TaggedObject.GetInstance(seq[1]).GetObject());
+                    m_publicationInfo = PkiPublicationInfo.GetInstance(Asn1TaggedObject.GetInstance(seq[2]).GetObject());
 				}
 			}
 		}
 
-		public static CertifiedKeyPair GetInstance(object obj)
+		public CertifiedKeyPair(CertOrEncCert certOrEncCert)
+			: this(certOrEncCert, (EncryptedKey)null, null)
 		{
-			if (obj is CertifiedKeyPair)
-				return (CertifiedKeyPair)obj;
-
-			if (obj is Asn1Sequence)
-				return new CertifiedKeyPair((Asn1Sequence)obj);
-
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
 		}
 
-		public CertifiedKeyPair(
-			CertOrEncCert certOrEncCert)
-			: this(certOrEncCert, null, null)
-		{
-		}
+        public CertifiedKeyPair(CertOrEncCert certOrEncCert, EncryptedValue privateKey,
+            PkiPublicationInfo publicationInfo)
+            : this(certOrEncCert, privateKey == null ? null : new EncryptedKey(privateKey), publicationInfo)
+        {
+        }
 
-		public CertifiedKeyPair(
-			CertOrEncCert		certOrEncCert,
-			EncryptedValue		privateKey,
-			PkiPublicationInfo	publicationInfo
-		)
-		{
+        public CertifiedKeyPair(CertOrEncCert certOrEncCert, EncryptedKey privateKey,
+			PkiPublicationInfo publicationInfo)
+        {
 			if (certOrEncCert == null)
-				throw new ArgumentNullException("certOrEncCert");
+				throw new ArgumentNullException(nameof(certOrEncCert));
 
-			this.certOrEncCert = certOrEncCert;
-			this.privateKey = privateKey;
-			this.publicationInfo = publicationInfo;
-		}
+            m_certOrEncCert = certOrEncCert;
+            m_privateKey = privateKey;
+            m_publicationInfo = publicationInfo;
+        }
 
-		public virtual CertOrEncCert CertOrEncCert
-		{
-			get { return certOrEncCert; }
-		}
+		public virtual CertOrEncCert CertOrEncCert => m_certOrEncCert;
 
-		public virtual EncryptedValue PrivateKey
-		{
-			get { return privateKey; }
-		}
+		public virtual EncryptedKey PrivateKey => m_privateKey;
 
-		public virtual PkiPublicationInfo PublicationInfo
-		{
-			get { return publicationInfo; }
-		}
+		public virtual PkiPublicationInfo PublicationInfo => m_publicationInfo;
 
 		/**
 		 * <pre>
@@ -97,9 +89,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(certOrEncCert);
-            v.AddOptionalTagged(true, 0, privateKey);
-            v.AddOptionalTagged(true, 1, publicationInfo);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_certOrEncCert);
+            v.AddOptionalTagged(true, 0, m_privateKey);
+            v.AddOptionalTagged(true, 1, m_publicationInfo);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/Challenge.cs b/crypto/src/asn1/cmp/Challenge.cs
index 016c082e2..ca3d06339 100644
--- a/crypto/src/asn1/cmp/Challenge.cs
+++ b/crypto/src/asn1/cmp/Challenge.cs
@@ -1,16 +1,52 @@
 using System;
 
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class Challenge
+    /**
+     * <pre>
+     * Challenge ::= SEQUENCE {
+     *          owf                 AlgorithmIdentifier  OPTIONAL,
+     *
+     *          -- MUST be present in the first Challenge; MAY be omitted in
+     *          -- any subsequent Challenge in POPODecKeyChallContent (if
+     *          -- omitted, then the owf used in the immediately preceding
+     *          -- Challenge is to be used).
+     *
+     *          witness             OCTET STRING,
+     *          -- the result of applying the one-way function (owf) to a
+     *          -- randomly-generated INTEGER, A.  [Note that a different
+     *          -- INTEGER MUST be used for each Challenge.]
+     *          challenge           OCTET STRING
+     *          -- the encryption (under the public key for which the cert.
+     *          -- request is being made) of Rand, where Rand is specified as
+     *          --   Rand ::= SEQUENCE {
+     *          --      int      INTEGER,
+     *          --       - the randomly-generated INTEGER A (above)
+     *          --      sender   GeneralName
+     *          --       - the sender's name (as included in PKIHeader)
+     *          --   }
+     *      }
+     *      </pre>
+     */
+    public class Challenge
 		: Asn1Encodable
 	{
-		private readonly AlgorithmIdentifier owf;
-		private readonly Asn1OctetString witness;
-		private readonly Asn1OctetString challenge;
+        public static Challenge GetInstance(object obj)
+        {
+            if (obj is Challenge challenge)
+                return challenge;
+
+            if (obj != null)
+                return new Challenge(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly AlgorithmIdentifier m_owf;
+		private readonly Asn1OctetString m_witness;
+		private readonly Asn1OctetString m_challenge;
 
 		private Challenge(Asn1Sequence seq)
 		{
@@ -18,30 +54,32 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 			if (seq.Count == 3)
 			{
-				owf = AlgorithmIdentifier.GetInstance(seq[index++]);
+				m_owf = AlgorithmIdentifier.GetInstance(seq[index++]);
 			}
 
-			witness = Asn1OctetString.GetInstance(seq[index++]);
-			challenge = Asn1OctetString.GetInstance(seq[index]);
+			m_witness = Asn1OctetString.GetInstance(seq[index++]);
+			m_challenge = Asn1OctetString.GetInstance(seq[index]);
 		}
 
-		public static Challenge GetInstance(object obj)
-		{
-			if (obj is Challenge)
-				return (Challenge)obj;
+        public Challenge(byte[] witness, byte[] challenge)
+            : this(null, witness, challenge)
+        {
+        }
 
-			if (obj is Asn1Sequence)
-				return new Challenge((Asn1Sequence)obj);
+        public Challenge(AlgorithmIdentifier owf, byte[] witness, byte[] challenge)
+        {
+            m_owf = owf;
+            m_witness = new DerOctetString(witness);
+            m_challenge = new DerOctetString(challenge);
+        }
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
+        public virtual AlgorithmIdentifier Owf => m_owf;
 
-		public virtual AlgorithmIdentifier Owf
-		{
-			get { return owf; }
-		}
+		public virtual Asn1OctetString Witness => m_witness;
+
+		public virtual Asn1OctetString ChallengeValue => m_challenge;
 
-		/**
+        /**
 		 * <pre>
 		 * Challenge ::= SEQUENCE {
 		 *                 owf                 AlgorithmIdentifier  OPTIONAL,
@@ -68,12 +106,57 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 * </pre>
 		 * @return a basic ASN.1 object representation.
 		 */
-		public override Asn1Object ToAsn1Object()
+        public override Asn1Object ToAsn1Object()
 		{
 			Asn1EncodableVector v = new Asn1EncodableVector();
-			v.AddOptional(owf);
-			v.Add(witness, challenge);
+			v.AddOptional(m_owf);
+			v.Add(m_witness, m_challenge);
 			return new DerSequence(v);
 		}
+
+        /**
+         * Rand is the inner type
+         */
+        public class Rand
+            : Asn1Encodable
+        {
+            public static Rand GetInstance(object obj)
+            {
+                if (obj is Rand rand)
+                    return rand;
+
+                if (obj != null)
+                    return new Rand(Asn1Sequence.GetInstance(obj));
+
+                return null;
+            }
+
+            private readonly DerInteger m_intVal;
+            private readonly GeneralName m_sender;
+
+            public Rand(DerInteger intVal, GeneralName sender)
+            {
+                m_intVal = intVal;
+                m_sender = sender;
+            }
+
+            public Rand(Asn1Sequence seq)
+            {
+                if (seq.Count != 2)
+                    throw new ArgumentException("expected sequence size of 2");
+
+                m_intVal = DerInteger.GetInstance(seq[0]);
+                m_sender = GeneralName.GetInstance(seq[1]);
+            }
+
+            public virtual DerInteger IntVal => m_intVal;
+
+			public virtual GeneralName Sender => m_sender;
+
+			public override Asn1Object ToAsn1Object()
+			{
+                return new DerSequence(m_intVal, m_sender);
+            }
+        }
 	}
 }
diff --git a/crypto/src/asn1/cmp/CmpCertificate.cs b/crypto/src/asn1/cmp/CmpCertificate.cs
index 33356b486..af433ec4d 100644
--- a/crypto/src/asn1/cmp/CmpCertificate.cs
+++ b/crypto/src/asn1/cmp/CmpCertificate.cs
@@ -1,4 +1,5 @@
 using System;
+using System.IO;
 
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Utilities;
@@ -8,54 +9,84 @@ namespace Org.BouncyCastle.Asn1.Cmp
     public class CmpCertificate
         : Asn1Encodable, IAsn1Choice
     {
-        private readonly X509CertificateStructure x509v3PKCert;
-        private readonly AttributeCertificate x509v2AttrCert;
-
-        /**
-         * Note: the addition of attribute certificates is a BC extension.
-         */
-        public CmpCertificate(AttributeCertificate x509v2AttrCert)
+        public static CmpCertificate GetInstance(object obj)
         {
-            this.x509v2AttrCert = x509v2AttrCert;
-        }
+            // TODO[cmp] Review this whole metho
 
-        public CmpCertificate(X509CertificateStructure x509v3PKCert)
-        {
-            if (x509v3PKCert.Version != 3)
-                throw new ArgumentException("only version 3 certificates allowed", "x509v3PKCert");
+            if (obj == null)
+                return null;
 
-            this.x509v3PKCert = x509v3PKCert;
-        }
+            if (obj is CmpCertificate cmpCertificate)
+                return cmpCertificate;
 
-        public static CmpCertificate GetInstance(object obj)
-        {
-            if (obj is CmpCertificate)
-                return (CmpCertificate)obj;
+            if (obj is byte[] bs)
+            {
+                try
+                {
+                    obj = Asn1Object.FromByteArray(bs);
+                }
+                catch (IOException)
+                {
+                    throw new ArgumentException("Invalid encoding in CmpCertificate");
+                }
+            }
 
             if (obj is Asn1Sequence)
                 return new CmpCertificate(X509CertificateStructure.GetInstance(obj));
 
-            if (obj is Asn1TaggedObject)
-                return new CmpCertificate(AttributeCertificate.GetInstance(((Asn1TaggedObject)obj).GetObject()));
+            if (obj is Asn1TaggedObject taggedObject)
+                return new CmpCertificate(taggedObject.TagNo, taggedObject.GetObject());
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
         }
 
-        public virtual bool IsX509v3PKCert
+        public static CmpCertificate GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
         {
-            get { return x509v3PKCert != null; }
+            // TODO[cmp]
+            if (taggedObject == null)
+                return null;
+
+            if (!declaredExplicit)
+                throw new ArgumentException("tag must be explicit");
+
+            // TODO[cmp]
+            return GetInstance(taggedObject.GetObject());
         }
 
-        public virtual X509CertificateStructure X509v3PKCert
+        private readonly X509CertificateStructure m_x509v3PKCert;
+
+        private readonly int m_otherTagValue;
+        private readonly Asn1Encodable m_otherCert;
+
+        /**
+         * Note: the addition of other certificates is a BC extension. If you use this constructor they
+         * will be added with an explicit tag value of type.
+         *
+         * @param type      the type of the certificate (used as a tag value).
+         * @param otherCert the object representing the certificate
+         */
+        public CmpCertificate(int type, Asn1Encodable otherCert)
         {
-            get { return x509v3PKCert; }
+            m_otherTagValue = type;
+            m_otherCert = otherCert;
         }
 
-        public virtual AttributeCertificate X509v2AttrCert
+        public CmpCertificate(X509CertificateStructure x509v3PKCert)
         {
-            get { return x509v2AttrCert; }
+            if (x509v3PKCert.Version != 3)
+                throw new ArgumentException("only version 3 certificates allowed", nameof(x509v3PKCert));
+
+            m_x509v3PKCert = x509v3PKCert;
         }
 
+        public virtual bool IsX509v3PKCert => m_x509v3PKCert != null;
+
+        public virtual X509CertificateStructure X509v3PKCert => m_x509v3PKCert;
+
+        public virtual int OtherCertTag => m_otherTagValue;
+
+        public virtual Asn1Encodable OtherCert => m_otherCert;
+
         /**
          * <pre>
          * CMPCertificate ::= CHOICE {
@@ -69,13 +100,13 @@ namespace Org.BouncyCastle.Asn1.Cmp
          */
         public override Asn1Object ToAsn1Object()
         {
-            if (x509v2AttrCert != null)
+            if (m_otherCert != null)
             {
                 // explicit following CMP conventions
-                return new DerTaggedObject(true, 1, x509v2AttrCert);
+                return new DerTaggedObject(true, m_otherTagValue, m_otherCert);
             }
 
-            return x509v3PKCert.ToAsn1Object();
+            return m_x509v3PKCert.ToAsn1Object();
         }
     }
 }
diff --git a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
index 7e8274175..1b3227c47 100644
--- a/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
+++ b/crypto/src/asn1/cmp/CmpObjectIdentifiers.cs
@@ -2,105 +2,257 @@ using System;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public abstract class CmpObjectIdentifiers
+	public static class CmpObjectIdentifiers
 	{
-		// RFC 4210
-
-		// id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13}
-		public static readonly DerObjectIdentifier passwordBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.13");
-
-		// id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30}
-		public static readonly DerObjectIdentifier dhBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.30");
-
-		// Example InfoTypeAndValue contents include, but are not limited
-		// to, the following (un-comment in this ASN.1 module and use as
-		// appropriate for a given environment):
-		//
-		// id-it-caProtEncCert OBJECT IDENTIFIER ::= {id-it 1}
-		// CAProtEncCertValue ::= CMPCertificate
-		// id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2}
-		// SignKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
-		// id-it-encKeyPairTypes OBJECT IDENTIFIER ::= {id-it 3}
-		// EncKeyPairTypesValue ::= SEQUENCE OF AlgorithmIdentifier
-		// id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4}
-		// PreferredSymmAlgValue ::= AlgorithmIdentifier
-		// id-it-caKeyUpdateInfo OBJECT IDENTIFIER ::= {id-it 5}
-		// CAKeyUpdateInfoValue ::= CAKeyUpdAnnContent
-		// id-it-currentCRL OBJECT IDENTIFIER ::= {id-it 6}
-		// CurrentCRLValue ::= CertificateList
-		// id-it-unsupportedOIDs OBJECT IDENTIFIER ::= {id-it 7}
-		// UnsupportedOIDsValue ::= SEQUENCE OF OBJECT IDENTIFIER
-		// id-it-keyPairParamReq OBJECT IDENTIFIER ::= {id-it 10}
-		// KeyPairParamReqValue ::= OBJECT IDENTIFIER
-		// id-it-keyPairParamRep OBJECT IDENTIFIER ::= {id-it 11}
-		// KeyPairParamRepValue ::= AlgorithmIdentifer
-		// id-it-revPassphrase OBJECT IDENTIFIER ::= {id-it 12}
-		// RevPassphraseValue ::= EncryptedValue
-		// id-it-implicitConfirm OBJECT IDENTIFIER ::= {id-it 13}
-		// ImplicitConfirmValue ::= NULL
-		// id-it-confirmWaitTime OBJECT IDENTIFIER ::= {id-it 14}
-		// ConfirmWaitTimeValue ::= GeneralizedTime
-		// id-it-origPKIMessage OBJECT IDENTIFIER ::= {id-it 15}
-		// OrigPKIMessageValue ::= PKIMessages
-		// id-it-suppLangTags OBJECT IDENTIFIER ::= {id-it 16}
-		// SuppLangTagsValue ::= SEQUENCE OF UTF8String
-		//
-		// where
-		//
-		// id-pkix OBJECT IDENTIFIER ::= {
-		// iso(1) identified-organization(3)
-		// dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
-		// and
-		// id-it OBJECT IDENTIFIER ::= {id-pkix 4}
-		public static readonly DerObjectIdentifier it_caProtEncCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.1");
-		public static readonly DerObjectIdentifier it_signKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.2");
-		public static readonly DerObjectIdentifier it_encKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.3");
-		public static readonly DerObjectIdentifier it_preferredSymAlg = new DerObjectIdentifier("1.3.6.1.5.5.7.4.4");
-		public static readonly DerObjectIdentifier it_caKeyUpdateInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.4.5");
-		public static readonly DerObjectIdentifier it_currentCRL = new DerObjectIdentifier("1.3.6.1.5.5.7.4.6");
-		public static readonly DerObjectIdentifier it_unsupportedOIDs = new DerObjectIdentifier("1.3.6.1.5.5.7.4.7");
-		public static readonly DerObjectIdentifier it_keyPairParamReq = new DerObjectIdentifier("1.3.6.1.5.5.7.4.10");
-		public static readonly DerObjectIdentifier it_keyPairParamRep = new DerObjectIdentifier("1.3.6.1.5.5.7.4.11");
-		public static readonly DerObjectIdentifier it_revPassphrase = new DerObjectIdentifier("1.3.6.1.5.5.7.4.12");
-		public static readonly DerObjectIdentifier it_implicitConfirm = new DerObjectIdentifier("1.3.6.1.5.5.7.4.13");
-		public static readonly DerObjectIdentifier it_confirmWaitTime = new DerObjectIdentifier("1.3.6.1.5.5.7.4.14");
-		public static readonly DerObjectIdentifier it_origPKIMessage = new DerObjectIdentifier("1.3.6.1.5.5.7.4.15");
-		public static readonly DerObjectIdentifier it_suppLangTags = new DerObjectIdentifier("1.3.6.1.5.5.7.4.16");
-
-		// RFC 4211
-
-		// id-pkix OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
-		// dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
-		//
-		// arc for Internet X.509 PKI protocols and their components
-		// id-pkip OBJECT IDENTIFIER :: { id-pkix pkip(5) }
-		//
-		// arc for Registration Controls in CRMF
-		// id-regCtrl OBJECT IDENTIFIER ::= { id-pkip regCtrl(1) }
-		//
-		// arc for Registration Info in CRMF
-		// id-regInfo OBJECT IDENTIFIER ::= { id-pkip id-regInfo(2) }
-
-		public static readonly DerObjectIdentifier regCtrl_regToken = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.1");
-		public static readonly DerObjectIdentifier regCtrl_authenticator = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.2");
-		public static readonly DerObjectIdentifier regCtrl_pkiPublicationInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.3");
-		public static readonly DerObjectIdentifier regCtrl_pkiArchiveOptions = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.4");
-		public static readonly DerObjectIdentifier regCtrl_oldCertID = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.5");
-		public static readonly DerObjectIdentifier regCtrl_protocolEncrKey = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.6");
-
-		// From RFC4210:
-		// id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}
-		public static readonly DerObjectIdentifier regCtrl_altCertTemplate = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.7");
-
-		public static readonly DerObjectIdentifier regInfo_utf8Pairs = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.1");
-		public static readonly DerObjectIdentifier regInfo_certReq = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.2");
-
-		// id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-		// us(840) rsadsi(113549) pkcs(1) pkcs9(9) 16 }
-		//
-		// id-ct OBJECT IDENTIFIER ::= { id-smime 1 } -- content types
-		//
-		// id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
-		public static readonly DerObjectIdentifier ct_encKeyWithID = new DerObjectIdentifier("1.2.840.113549.1.9.16.1.21");
+        // RFC 4210
+
+        /**
+         * id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13}
+         */
+        public static readonly DerObjectIdentifier passwordBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.13");
+
+        /**
+         * id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30}
+         */
+        public static readonly DerObjectIdentifier dhBasedMac = new DerObjectIdentifier("1.2.840.113533.7.66.30");
+
+        // Example InfoTypeAndValue contents include, but are not limited
+        // to, the following (un-comment in this ASN.1 module and use as
+        // appropriate for a given environment):
+        //
+        //   id-it-caProtEncCert    OBJECT IDENTIFIER ::= {id-it 1}
+        //      CAProtEncCertValue      ::= CMPCertificate
+        //   id-it-signKeyPairTypes OBJECT IDENTIFIER ::= {id-it 2}
+        //      SignKeyPairTypesValue   ::= SEQUENCE OF AlgorithmIdentifier
+        //   id-it-encKeyPairTypes  OBJECT IDENTIFIER ::= {id-it 3}
+        //      EncKeyPairTypesValue    ::= SEQUENCE OF AlgorithmIdentifier
+        //   id-it-preferredSymmAlg OBJECT IDENTIFIER ::= {id-it 4}
+        //      PreferredSymmAlgValue   ::= AlgorithmIdentifier
+        //   id-it-caKeyUpdateInfo  OBJECT IDENTIFIER ::= {id-it 5}
+        //      CAKeyUpdateInfoValue    ::= CAKeyUpdAnnContent
+        //   id-it-currentCRL       OBJECT IDENTIFIER ::= {id-it 6}
+        //      CurrentCRLValue         ::= CertificateList
+        //   id-it-unsupportedOIDs  OBJECT IDENTIFIER ::= {id-it 7}
+        //      UnsupportedOIDsValue    ::= SEQUENCE OF OBJECT IDENTIFIER
+        //   id-it-keyPairParamReq  OBJECT IDENTIFIER ::= {id-it 10}
+        //      KeyPairParamReqValue    ::= OBJECT IDENTIFIER
+        //   id-it-keyPairParamRep  OBJECT IDENTIFIER ::= {id-it 11}
+        //      KeyPairParamRepValue    ::= AlgorithmIdentifer
+        //   id-it-revPassphrase    OBJECT IDENTIFIER ::= {id-it 12}
+        //      RevPassphraseValue      ::= EncryptedValue
+        //   id-it-implicitConfirm  OBJECT IDENTIFIER ::= {id-it 13}
+        //      ImplicitConfirmValue    ::= NULL
+        //   id-it-confirmWaitTime  OBJECT IDENTIFIER ::= {id-it 14}
+        //      ConfirmWaitTimeValue    ::= GeneralizedTime
+        //   id-it-origPKIMessage   OBJECT IDENTIFIER ::= {id-it 15}
+        //      OrigPKIMessageValue     ::= PKIMessages
+        //   id-it-suppLangTags     OBJECT IDENTIFIER ::= {id-it 16}
+        //      SuppLangTagsValue       ::= SEQUENCE OF UTF8String
+        //   id-it-certProfile  OBJECT IDENTIFIER ::= {id-it 21}
+        //      CertProfileValue ::= SEQUENCE SIZE (1..MAX) OF UTF8String
+        // where
+        //
+        //   id-pkix OBJECT IDENTIFIER ::= {
+        //      iso(1) identified-organization(3)
+        //      dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
+        // and
+        //   id-it   OBJECT IDENTIFIER ::= {id-pkix 4}
+
+        /** RFC 4120: it-id: PKIX.4 = 1.3.6.1.5.5.7.4 */
+
+
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.1
+         */
+        public static readonly DerObjectIdentifier it_caProtEncCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.1");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.2
+         */
+        public static readonly DerObjectIdentifier it_signKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.2");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.3
+         */
+        public static readonly DerObjectIdentifier it_encKeyPairTypes = new DerObjectIdentifier("1.3.6.1.5.5.7.4.3");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.4
+         */
+        public static readonly DerObjectIdentifier it_preferredSymAlg = new DerObjectIdentifier("1.3.6.1.5.5.7.4.4");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.5
+         */
+        public static readonly DerObjectIdentifier it_caKeyUpdateInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.4.5");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.6
+         */
+        public static readonly DerObjectIdentifier it_currentCRL = new DerObjectIdentifier("1.3.6.1.5.5.7.4.6");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.7
+         */
+        public static readonly DerObjectIdentifier it_unsupportedOIDs = new DerObjectIdentifier("1.3.6.1.5.5.7.4.7");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.10
+         */
+        public static readonly DerObjectIdentifier it_keyPairParamReq = new DerObjectIdentifier("1.3.6.1.5.5.7.4.10");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.11
+         */
+        public static readonly DerObjectIdentifier it_keyPairParamRep = new DerObjectIdentifier("1.3.6.1.5.5.7.4.11");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.12
+         */
+        public static readonly DerObjectIdentifier it_revPassphrase = new DerObjectIdentifier("1.3.6.1.5.5.7.4.12");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.13
+         */
+        public static readonly DerObjectIdentifier it_implicitConfirm = new DerObjectIdentifier("1.3.6.1.5.5.7.4.13");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.14
+         */
+        public static readonly DerObjectIdentifier it_confirmWaitTime = new DerObjectIdentifier("1.3.6.1.5.5.7.4.14");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.15
+         */
+        public static readonly DerObjectIdentifier it_origPKIMessage = new DerObjectIdentifier("1.3.6.1.5.5.7.4.15");
+        /**
+         * RFC 4120: 1.3.6.1.5.5.7.4.16
+         */
+        public static readonly DerObjectIdentifier it_suppLangTags = new DerObjectIdentifier("1.3.6.1.5.5.7.4.16");
+
+        /**
+         * Update 16, RFC 4210
+         * {id-it 17}
+         */
+        public static readonly DerObjectIdentifier id_it_caCerts = new DerObjectIdentifier("1.3.6.1.5.5.7.4.17");
+
+
+        /**
+         * Update 16, RFC 4210
+         * GenRep:    {id-it 18}, RootCaKeyUpdateContent
+         */
+        public static readonly DerObjectIdentifier id_it_rootCaKeyUpdate = new DerObjectIdentifier("1.3.6.1.5.5.7.4.18");
+
+
+        /**
+         * Update 16, RFC 4210
+         * {id-it 19}
+         */
+        public static readonly DerObjectIdentifier id_it_certReqTemplate = new DerObjectIdentifier("1.3.6.1.5.5.7.4.19");
+
+
+        /**
+         * Update 16, RFC 4210
+         * GenMsg:    {id-it 20}, RootCaCertValue
+         */
+        public static readonly DerObjectIdentifier id_it_rootCaCert = new DerObjectIdentifier("1.3.6.1.5.5.7.4.20");
+
+        /**
+         * Update-16 to RFC 4210
+         * id-it-certProfile  OBJECT IDENTIFIER ::= {id-it 21}
+         */
+        public static readonly DerObjectIdentifier id_it_certProfile = new DerObjectIdentifier("1.3.6.1.5.5.7.4.21");
+
+        public static readonly DerObjectIdentifier id_it_crlStatusList = new DerObjectIdentifier("1.3.6.1.5.5.7.4.22");
+
+        public static readonly DerObjectIdentifier id_it_crls = new DerObjectIdentifier("1.3.6.1.5.5.7.4.23");
+
+        // Not yet formally defined.
+
+        //public static readonly DerObjectIdentifier id_it_crlStatusList = null;
+        //public static readonly DerObjectIdentifier id_it_crls = null;
+
+
+        // RFC 4211
+
+        // id-pkix  OBJECT IDENTIFIER  ::= { iso(1) identified-organization(3)
+        //     dod(6) internet(1) security(5) mechanisms(5) pkix(7) }
+        //
+        // arc for Internet X.509 PKI protocols and their components
+        // id-pkip  OBJECT IDENTIFIER :: { id-pkix pkip(5) }
+        //
+        // arc for Registration Controls in CRMF
+        // id-regCtrl  OBJECT IDENTIFIER ::= { id-pkip regCtrl(1) }
+        //
+        // arc for Registration Info in CRMF
+        // id-regInfo       OBJECT IDENTIFIER ::= { id-pkip id-regInfo(2) }
+
+        /**
+         * RFC 4211: it-pkip: PKIX.5 = 1.3.6.1.5.5.7.5
+         */
+        public static readonly DerObjectIdentifier id_pkip = new DerObjectIdentifier("1.3.6.1.5.5.7.5");
+
+        /**
+         * RFC 4211: it-regCtrl: 1.3.6.1.5.5.7.5.1
+         */
+        public static readonly DerObjectIdentifier id_regCtrl = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1");
+        /**
+         * RFC 4211: it-regInfo: 1.3.6.1.5.5.7.5.2
+         */
+        public static readonly DerObjectIdentifier id_regInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2");
+
+
+        /**
+         * 1.3.6.1.5.5.7.5.1.1
+         */
+        public static readonly DerObjectIdentifier regCtrl_regToken = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.1");
+        /**
+         * 1.3.6.1.5.5.7.5.1.2
+         */
+        public static readonly DerObjectIdentifier regCtrl_authenticator = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.2");
+        /**
+         * 1.3.6.1.5.5.7.5.1.3
+         */
+        public static readonly DerObjectIdentifier regCtrl_pkiPublicationInfo = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.3");
+        /**
+         * 1.3.6.1.5.5.7.5.1.4
+         */
+        public static readonly DerObjectIdentifier regCtrl_pkiArchiveOptions = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.4");
+        /**
+         * 1.3.6.1.5.5.7.5.1.5
+         */
+        public static readonly DerObjectIdentifier regCtrl_oldCertID = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.5");
+        /**
+         * 1.3.6.1.5.5.7.5.1.6
+         */
+        public static readonly DerObjectIdentifier regCtrl_protocolEncrKey = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.6");
+
+        /**
+         * From RFC4210:
+         * id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}; 1.3.6.1.5.5.7.1.7
+         */
+        public static readonly DerObjectIdentifier regCtrl_altCertTemplate = new DerObjectIdentifier("1.3.6.1.5.5.7.5.1.7");
+
+        /**
+         * RFC 4211: it-regInfo-utf8Pairs: 1.3.6.1.5.5.7.5.2.1
+         */
+        public static readonly DerObjectIdentifier regInfo_utf8Pairs = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.1");
+        /**
+         * RFC 4211: it-regInfo-certReq: 1.3.6.1.5.5.7.5.2.1
+         */
+        public static readonly DerObjectIdentifier regInfo_certReq = new DerObjectIdentifier("1.3.6.1.5.5.7.5.2.2");
+
+        /**
+         * 1.2.840.113549.1.9.16.1.21
+         * <p>
+         * id-ct   OBJECT IDENTIFIER ::= { id-smime  1 }  -- content types
+         * </p><p>
+         * id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
+         * </p>
+         */
+        public static readonly DerObjectIdentifier ct_encKeyWithID = new DerObjectIdentifier("1.2.840.113549.1.9.16.1.21");
+
+
+        /**
+         * id-regCtrl-algId OBJECT IDENTIFIER ::= { iso(1)
+         * identified-organization(3) dod(6) internet(1) security(5)
+         * mechanisms(5) pkix(7) pkip(5) regCtrl(1) 11 }
+         */
+        public static readonly DerObjectIdentifier id_regCtrl_algId = id_pkip.Branch("1.11");
+
+        /**
+         * id-regCtrl-rsaKeyLen OBJECT IDENTIFIER ::= { iso(1)
+         * identified-organization(3) dod(6) internet(1) security(5)
+         * mechanisms(5) pkix(7) pkip(5) regCtrl(1) 12 }
+         */
+        public static readonly DerObjectIdentifier id_regCtrl_rsaKeyLen = id_pkip.Branch("1.12");
 	}
 }
diff --git a/crypto/src/asn1/cmp/CrlAnnContent.cs b/crypto/src/asn1/cmp/CrlAnnContent.cs
index db8ecfa40..0da25cd0e 100644
--- a/crypto/src/asn1/cmp/CrlAnnContent.cs
+++ b/crypto/src/asn1/cmp/CrlAnnContent.cs
@@ -1,39 +1,36 @@
-using System;
-
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class CrlAnnContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
+        public static CrlAnnContent GetInstance(object obj)
+        {
+			if (obj is CrlAnnContent crlAnnContent)
+				return crlAnnContent;
 
-		private CrlAnnContent(Asn1Sequence seq)
-		{
-			content = seq;
-		}
+			if (obj != null)
+				return new CrlAnnContent(Asn1Sequence.GetInstance(obj));
 
-		public static CrlAnnContent GetInstance(object obj)
-		{
-			if (obj is CrlAnnContent)
-				return (CrlAnnContent)obj;
+			return null;
+        }
 
-			if (obj is Asn1Sequence)
-				return new CrlAnnContent((Asn1Sequence)obj);
+        private readonly Asn1Sequence m_content;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+		private CrlAnnContent(Asn1Sequence seq)
+		{
+			m_content = seq;
 		}
 
-		public virtual CertificateList[] ToCertificateListArray()
+        public CrlAnnContent(CertificateList crl)
+        {
+            m_content = new DerSequence(crl);
+        }
+
+        public virtual CertificateList[] ToCertificateListArray()
 		{
-			CertificateList[] result = new CertificateList[content.Count];
-			for (int i = 0; i != result.Length; ++ i)
-			{
-				result[i] = CertificateList.GetInstance(content[i]);
-			}
-			return result;
+			return m_content.MapElements(CertificateList.GetInstance);
 		}
 
 		/**
@@ -44,7 +41,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return content;
+			return m_content;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/CrlSource.cs b/crypto/src/asn1/cmp/CrlSource.cs
new file mode 100644
index 000000000..9e2526ec2
--- /dev/null
+++ b/crypto/src/asn1/cmp/CrlSource.cs
@@ -0,0 +1,72 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * GenMsg:    {id-it TBD1}, SEQUENCE SIZE (1..MAX) OF CRLStatus
+     * GenRep:    {id-it TBD2}, SEQUENCE SIZE (1..MAX) OF
+     * CertificateList  |  &lt; absent &gt;
+     * <p>
+     * CRLSource ::= CHOICE {
+     * dpn          [0] DistributionPointName,
+     * issuer       [1] GeneralNames }
+     * </p>
+     */
+    public class CrlSource
+        : Asn1Encodable, IAsn1Choice
+    {
+        public static CrlSource GetInstance(object obj)
+        {
+            if (obj is CrlSource crlSource)
+                return crlSource;
+
+            if (obj != null)
+                return new CrlSource(Asn1TaggedObject.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly DistributionPointName m_dpn;
+        private readonly GeneralNames m_issuer;
+
+        private CrlSource(Asn1TaggedObject taggedObject)
+        {
+            switch (taggedObject.TagNo)
+            {
+            case 0:
+                m_dpn = DistributionPointName.GetInstance(taggedObject, true);
+                m_issuer = null;
+                break;
+            case 1:
+                m_dpn = null;
+                m_issuer = GeneralNames.GetInstance(taggedObject, true);
+                break;
+            default:
+                throw new ArgumentException("unknown tag: " + Asn1Utilities.GetTagText(taggedObject));
+            }
+        }
+
+        public CrlSource(DistributionPointName dpn, GeneralNames issuer)
+        {
+            if ((dpn == null) == (issuer == null))
+                throw new ArgumentException("either dpn or issuer must be set");
+
+            m_dpn = dpn;
+            m_issuer = issuer;
+        }
+
+        public virtual DistributionPointName Dpn => m_dpn;
+
+        public virtual GeneralNames Issuer => m_issuer;
+
+        public override Asn1Object ToAsn1Object()
+        {
+            if (m_dpn != null)
+                return new DerTaggedObject(true, 0, m_dpn);
+
+            return new DerTaggedObject(true, 1, m_issuer);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/CrlStatus.cs b/crypto/src/asn1/cmp/CrlStatus.cs
new file mode 100644
index 000000000..5bacbbbcc
--- /dev/null
+++ b/crypto/src/asn1/cmp/CrlStatus.cs
@@ -0,0 +1,61 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * CRLStatus ::= SEQUENCE {
+     * source       CRLSource,
+     * thisUpdate   Time OPTIONAL }
+     */
+    public class CrlStatus
+        : Asn1Encodable
+    {
+        public static CrlStatus GetInstance(object obj)
+        {
+            if (obj is CrlStatus crlStatus)
+                return crlStatus;
+
+            if (obj != null)
+                return new CrlStatus(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly CrlSource m_source;
+        private readonly Time m_thisUpdate;
+
+        private CrlStatus(Asn1Sequence sequence)
+        {
+            int count = sequence.Count;
+            if (count < 1 || count > 2)
+                throw new ArgumentException("expected sequence size of 1 or 2, got " + count);
+
+            m_source = CrlSource.GetInstance(sequence[0]);
+
+            if (sequence.Count == 2)
+            {
+                m_thisUpdate = Time.GetInstance(sequence[1]);
+            }
+        }
+
+        public CrlStatus(CrlSource source, Time thisUpdate)
+        {
+            m_source = source;
+            m_thisUpdate = thisUpdate;
+        }
+
+        public virtual CrlSource Source => m_source;
+
+        public virtual Time ThisUpdate => m_thisUpdate;
+
+        public override Asn1Object ToAsn1Object()
+        {
+            if (m_thisUpdate == null)
+                return new DerSequence(m_source);
+
+            return new DerSequence(m_source, m_thisUpdate);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/DhbmParameter.cs b/crypto/src/asn1/cmp/DhbmParameter.cs
new file mode 100644
index 000000000..aaf71f70e
--- /dev/null
+++ b/crypto/src/asn1/cmp/DhbmParameter.cs
@@ -0,0 +1,56 @@
+using System;
+
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * DHBMParameter ::= SEQUENCE {
+     * owf                 AlgorithmIdentifier,
+     * -- AlgId for a One-Way Function (SHA-1 recommended)
+     * mac                 AlgorithmIdentifier
+     * -- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11],
+     * }   -- or HMAC [RFC2104, RFC2202])
+     */
+    public class DhbmParameter
+        : Asn1Encodable
+    {
+        public static DhbmParameter GetInstance(object obj)
+        {
+            if (obj is DhbmParameter dhbmParameter)
+                return dhbmParameter;
+
+            if (obj != null)
+                return new DhbmParameter(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly AlgorithmIdentifier m_owf;
+        private readonly AlgorithmIdentifier m_mac;
+
+        private DhbmParameter(Asn1Sequence sequence)
+        {
+            if (sequence.Count != 2)
+                throw new ArgumentException("expecting sequence size of 2");
+
+            m_owf = AlgorithmIdentifier.GetInstance(sequence[0]);
+            m_mac = AlgorithmIdentifier.GetInstance(sequence[1]);
+        }
+
+        public DhbmParameter(AlgorithmIdentifier owf, AlgorithmIdentifier mac)
+        {
+            m_owf = owf;
+            m_mac = mac;
+        }
+
+        public virtual AlgorithmIdentifier Owf => m_owf;
+
+        public virtual AlgorithmIdentifier Mac => m_mac;
+
+        public override Asn1Object ToAsn1Object()
+        {
+            return new DerSequence(m_owf, m_mac);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/ErrorMsgContent.cs b/crypto/src/asn1/cmp/ErrorMsgContent.cs
index 5d2132bb8..fe8318aab 100644
--- a/crypto/src/asn1/cmp/ErrorMsgContent.cs
+++ b/crypto/src/asn1/cmp/ErrorMsgContent.cs
@@ -1,45 +1,54 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class ErrorMsgContent
+    /**
+     * <pre>
+     *      ErrorMsgContent ::= SEQUENCE {
+     *          pKIStatusInfo          PKIStatusInfo,
+     *          errorCode              INTEGER           OPTIONAL,
+     *          -- implementation-specific error codes
+     *          errorDetails           PKIFreeText       OPTIONAL
+     *          -- implementation-specific error details
+     *      }
+     * </pre>
+     */
+    public class ErrorMsgContent
 		: Asn1Encodable
 	{
-		private readonly PkiStatusInfo pkiStatusInfo;
-		private readonly DerInteger errorCode;
-		private readonly PkiFreeText errorDetails;
+        public static ErrorMsgContent GetInstance(object obj)
+        {
+            if (obj is ErrorMsgContent errorMsgContent)
+                return errorMsgContent;
+
+            if (obj != null)
+                return new ErrorMsgContent(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly PkiStatusInfo m_pkiStatusInfo;
+		private readonly DerInteger m_errorCode;
+		private readonly PkiFreeText m_errorDetails;
 
 		private ErrorMsgContent(Asn1Sequence seq)
 		{
-			pkiStatusInfo = PkiStatusInfo.GetInstance(seq[0]);
+			m_pkiStatusInfo = PkiStatusInfo.GetInstance(seq[0]);
 
 			for (int pos = 1; pos < seq.Count; ++pos)
 			{
 				Asn1Encodable ae = seq[pos];
 				if (ae is DerInteger)
 				{
-					errorCode = DerInteger.GetInstance(ae);
+					m_errorCode = DerInteger.GetInstance(ae);
 				}
 				else
 				{
-					errorDetails = PkiFreeText.GetInstance(ae);
+					m_errorDetails = PkiFreeText.GetInstance(ae);
 				}
 			}
 		}
 
-		public static ErrorMsgContent GetInstance(object obj)
-		{
-			if (obj is ErrorMsgContent)
-				return (ErrorMsgContent)obj;
-
-			if (obj is Asn1Sequence)
-				return new ErrorMsgContent((Asn1Sequence)obj);
-
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
-
 		public ErrorMsgContent(PkiStatusInfo pkiStatusInfo)
 			: this(pkiStatusInfo, null, null)
 		{
@@ -51,27 +60,18 @@ namespace Org.BouncyCastle.Asn1.Cmp
 			PkiFreeText		errorDetails)
 		{
 			if (pkiStatusInfo == null)
-				throw new ArgumentNullException("pkiStatusInfo");
+				throw new ArgumentNullException(nameof(pkiStatusInfo));
 
-			this.pkiStatusInfo = pkiStatusInfo;
-			this.errorCode = errorCode;
-			this.errorDetails = errorDetails;
-		}
-		
-		public virtual PkiStatusInfo PkiStatusInfo
-		{
-			get { return pkiStatusInfo; }
+			m_pkiStatusInfo = pkiStatusInfo;
+			m_errorCode = errorCode;
+			m_errorDetails = errorDetails;
 		}
 
-		public virtual DerInteger ErrorCode
-		{
-			get { return errorCode; }
-		}
+		public virtual PkiStatusInfo PkiStatusInfo => m_pkiStatusInfo;
 
-		public virtual PkiFreeText ErrorDetails
-		{
-			get { return errorDetails; }
-		}
+		public virtual DerInteger ErrorCode => m_errorCode;
+
+		public virtual PkiFreeText ErrorDetails => m_errorDetails;
 
 		/**
 		 * <pre>
@@ -87,8 +87,8 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(pkiStatusInfo);
-			v.AddOptional(errorCode, errorDetails);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_pkiStatusInfo);
+			v.AddOptional(m_errorCode, m_errorDetails);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/GenMsgContent.cs b/crypto/src/asn1/cmp/GenMsgContent.cs
index f3142b5c6..b4673b76a 100644
--- a/crypto/src/asn1/cmp/GenMsgContent.cs
+++ b/crypto/src/asn1/cmp/GenMsgContent.cs
@@ -1,43 +1,42 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class GenMsgContent
+    /**
+     * <pre>GenMsgContent ::= SEQUENCE OF InfoTypeAndValue</pre>
+     */
+    public class GenMsgContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
+        public static GenMsgContent GetInstance(object obj)
+        {
+			if (obj is GenMsgContent genMsgContent)
+				return genMsgContent;
 
-		private GenMsgContent(Asn1Sequence seq)
-		{
-			content = seq;
-		}
+			if (obj != null)
+				return new GenMsgContent(Asn1Sequence.GetInstance(obj));
 
-		public static GenMsgContent GetInstance(object obj)
-		{
-			if (obj is GenMsgContent)
-				return (GenMsgContent)obj;
+			return null;
+        }
 
-			if (obj is Asn1Sequence)
-				return new GenMsgContent((Asn1Sequence)obj);
+        private readonly Asn1Sequence m_content;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+		private GenMsgContent(Asn1Sequence seq)
+		{
+			m_content = seq;
 		}
 
-		public GenMsgContent(params InfoTypeAndValue[] itv)
+        public GenMsgContent(InfoTypeAndValue itv)
+        {
+            m_content = new DerSequence(itv);
+        }
+
+        public GenMsgContent(params InfoTypeAndValue[] itvs)
 		{
-			content = new DerSequence(itv);
+			m_content = new DerSequence(itvs);
 		}
 
 		public virtual InfoTypeAndValue[] ToInfoTypeAndValueArray()
 		{
-			InfoTypeAndValue[] result = new InfoTypeAndValue[content.Count];
-			for (int i = 0; i != result.Length; ++i)
-			{
-				result[i] = InfoTypeAndValue.GetInstance(content[i]);
-			}
-			return result;
+			return m_content.MapElements(InfoTypeAndValue.GetInstance);
 		}
 
 		/**
@@ -48,7 +47,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return content;
+			return m_content;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/GenRepContent.cs b/crypto/src/asn1/cmp/GenRepContent.cs
index 3c3573e37..38f91061c 100644
--- a/crypto/src/asn1/cmp/GenRepContent.cs
+++ b/crypto/src/asn1/cmp/GenRepContent.cs
@@ -1,43 +1,39 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class GenRepContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
+        public static GenRepContent GetInstance(object obj)
+        {
+            if (obj is GenRepContent genRepContent)
+                return genRepContent;
 
-		private GenRepContent(Asn1Sequence seq)
-		{
-			content = seq;
-		}
+            if (obj != null)
+                return new GenRepContent(Asn1Sequence.GetInstance(obj));
 
-		public static GenRepContent GetInstance(object obj)
-		{
-			if (obj is GenRepContent)
-				return (GenRepContent)obj;
+            return null;
+        }
 
-			if (obj is Asn1Sequence)
-				return new GenRepContent((Asn1Sequence)obj);
+        private readonly Asn1Sequence m_content;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+		private GenRepContent(Asn1Sequence seq)
+		{
+			m_content = seq;
 		}
 
-		public GenRepContent(params InfoTypeAndValue[] itv)
+        public GenRepContent(InfoTypeAndValue itv)
+        {
+            m_content = new DerSequence(itv);
+        }
+
+        public GenRepContent(params InfoTypeAndValue[] itvs)
 		{
-			content = new DerSequence(itv);
+			m_content = new DerSequence(itvs);
 		}
 
 		public virtual InfoTypeAndValue[] ToInfoTypeAndValueArray()
 		{
-			InfoTypeAndValue[] result = new InfoTypeAndValue[content.Count];
-			for (int i = 0; i != result.Length; ++i)
-			{
-				result[i] = InfoTypeAndValue.GetInstance(content[i]);
-			}
-			return result;
+            return m_content.MapElements(InfoTypeAndValue.GetInstance);
 		}
 
 		/**
@@ -48,7 +44,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return content;
+			return m_content;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/InfoTypeAndValue.cs b/crypto/src/asn1/cmp/InfoTypeAndValue.cs
index 305d6e5e7..08ad68a42 100644
--- a/crypto/src/asn1/cmp/InfoTypeAndValue.cs
+++ b/crypto/src/asn1/cmp/InfoTypeAndValue.cs
@@ -50,54 +50,47 @@ namespace Org.BouncyCastle.Asn1.Cmp
     public class InfoTypeAndValue
         : Asn1Encodable
     {
-        private readonly DerObjectIdentifier infoType;
-        private readonly Asn1Encodable infoValue;
+        private readonly DerObjectIdentifier m_infoType;
+        private readonly Asn1Encodable m_infoValue;
 
         private InfoTypeAndValue(Asn1Sequence seq)
         {
-            infoType = DerObjectIdentifier.GetInstance(seq[0]);
+            m_infoType = DerObjectIdentifier.GetInstance(seq[0]);
 
             if (seq.Count > 1)
             {
-                infoValue = (Asn1Encodable)seq[1];
+                m_infoValue = seq[1];
             }
         }
 
         public static InfoTypeAndValue GetInstance(object obj)
         {
-            if (obj is InfoTypeAndValue)
-                return (InfoTypeAndValue)obj;
+            if (obj is InfoTypeAndValue infoTypeAndValue)
+                return infoTypeAndValue;
 
-            if (obj is Asn1Sequence)
-                return new InfoTypeAndValue((Asn1Sequence)obj);
+            if (obj != null)
+                return new InfoTypeAndValue(Asn1Sequence.GetInstance(obj));
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+            return null;
         }
 
-        public InfoTypeAndValue(
-            DerObjectIdentifier infoType)
+        public InfoTypeAndValue(DerObjectIdentifier infoType)
+            : this(infoType, null)
         {
-            this.infoType = infoType;
-            this.infoValue = null;
         }
 
-        public InfoTypeAndValue(
-            DerObjectIdentifier infoType,
-            Asn1Encodable       optionalValue)
+        public InfoTypeAndValue(DerObjectIdentifier infoType, Asn1Encodable infoValue)
         {
-            this.infoType = infoType;
-            this.infoValue = optionalValue;
-        }
+            if (infoType == null)
+                throw new ArgumentNullException(nameof(infoType));
 
-        public virtual DerObjectIdentifier InfoType
-        {
-            get { return infoType; }
+            m_infoType = infoType;
+            m_infoValue = infoValue;
         }
 
-        public virtual Asn1Encodable InfoValue
-        {
-            get { return infoValue; }
-        }
+        public virtual DerObjectIdentifier InfoType => m_infoType;
+
+        public virtual Asn1Encodable InfoValue => m_infoValue;
 
         /**
          * <pre>
@@ -110,9 +103,10 @@ namespace Org.BouncyCastle.Asn1.Cmp
          */
         public override Asn1Object ToAsn1Object()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(infoType);
-            v.AddOptional(infoValue);
-            return new DerSequence(v);
+            if (m_infoValue == null)
+                return new DerSequence(m_infoType);
+
+            return new DerSequence(m_infoType, m_infoValue);
         }
     }
 }
diff --git a/crypto/src/asn1/cmp/KeyRecRepContent.cs b/crypto/src/asn1/cmp/KeyRecRepContent.cs
index e35c0e351..6c5ef62f2 100644
--- a/crypto/src/asn1/cmp/KeyRecRepContent.cs
+++ b/crypto/src/asn1/cmp/KeyRecRepContent.cs
@@ -1,20 +1,29 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class KeyRecRepContent
 		: Asn1Encodable
 	{
-		private readonly PkiStatusInfo status;
-		private readonly CmpCertificate newSigCert;
-		private readonly Asn1Sequence caCerts;
-		private readonly Asn1Sequence keyPairHist;
+        public static KeyRecRepContent GetInstance(object obj)
+        {
+			if (obj is KeyRecRepContent keyRecRepContent)
+				return keyRecRepContent;
+
+			if (obj != null)
+				return new KeyRecRepContent(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly PkiStatusInfo m_status;
+		private readonly CmpCertificate m_newSigCert;
+		private readonly Asn1Sequence m_caCerts;
+		private readonly Asn1Sequence m_keyPairHist;
 
 		private KeyRecRepContent(Asn1Sequence seq)
 		{
-			status = PkiStatusInfo.GetInstance(seq[0]);
+			m_status = PkiStatusInfo.GetInstance(seq[0]);
 
 			for (int pos = 1; pos < seq.Count; ++pos)
 			{
@@ -22,66 +31,39 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 				switch (tObj.TagNo)
 				{
-					case 0:
-						newSigCert = CmpCertificate.GetInstance(tObj.GetObject());
-						break;
-					case 1:
-						caCerts = Asn1Sequence.GetInstance(tObj.GetObject());
-						break;
-					case 2:
-						keyPairHist = Asn1Sequence.GetInstance(tObj.GetObject());
-						break;
-					default:
-						throw new ArgumentException("unknown tag number: " + tObj.TagNo, "seq");
+				case 0:
+					m_newSigCert = CmpCertificate.GetInstance(tObj.GetObject());
+					break;
+				case 1:
+					m_caCerts = Asn1Sequence.GetInstance(tObj.GetObject());
+					break;
+				case 2:
+					m_keyPairHist = Asn1Sequence.GetInstance(tObj.GetObject());
+					break;
+				default:
+					throw new ArgumentException("unknown tag number: " + tObj.TagNo, "seq");
 				}
 			}
 		}
 
-		public static KeyRecRepContent GetInstance(object obj)
-		{
-			if (obj is KeyRecRepContent)
-				return (KeyRecRepContent)obj;
+		public virtual PkiStatusInfo Status => m_status;
 
-			if (obj is Asn1Sequence)
-				return new KeyRecRepContent((Asn1Sequence)obj);
-
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
-
-		public virtual PkiStatusInfo Status
-		{
-			get { return status; }
-		}
-
-		public virtual CmpCertificate NewSigCert
-		{
-			get { return newSigCert; }
-		}
+		public virtual CmpCertificate NewSigCert => m_newSigCert;
 
 		public virtual CmpCertificate[] GetCACerts()
 		{
-			if (caCerts == null)
+			if (m_caCerts == null)
 				return null;
 
-			CmpCertificate[] results = new CmpCertificate[caCerts.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = CmpCertificate.GetInstance(caCerts[i]);
-			}
-			return results;
+			return m_caCerts.MapElements(CmpCertificate.GetInstance);
 		}
 
 		public virtual CertifiedKeyPair[] GetKeyPairHist()
 		{
-			if (keyPairHist == null)
+			if (m_keyPairHist == null)
 				return null;
 
-			CertifiedKeyPair[] results = new CertifiedKeyPair[keyPairHist.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = CertifiedKeyPair.GetInstance(keyPairHist[i]);
-			}
-			return results;
+			return m_keyPairHist.MapElements(CertifiedKeyPair.GetInstance);
 		}
 
 		/**
@@ -99,10 +81,10 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(status);
-            v.AddOptionalTagged(true, 0, newSigCert);
-            v.AddOptionalTagged(true, 1, caCerts);
-            v.AddOptionalTagged(true, 2, keyPairHist);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_status);
+            v.AddOptionalTagged(true, 0, m_newSigCert);
+            v.AddOptionalTagged(true, 1, m_caCerts);
+            v.AddOptionalTagged(true, 2, m_keyPairHist);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/NestedMessageContent.cs b/crypto/src/asn1/cmp/NestedMessageContent.cs
new file mode 100644
index 000000000..0cb2c080b
--- /dev/null
+++ b/crypto/src/asn1/cmp/NestedMessageContent.cs
@@ -0,0 +1,35 @@
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * NestedMessageContent ::= PKIMessages
+     */
+    public class NestedMessageContent
+        : PkiMessages
+    {
+        public static new NestedMessageContent GetInstance(object obj)
+        {
+            if (obj is NestedMessageContent nestedMessageContent)
+                return nestedMessageContent;
+
+            if (obj != null)
+                return new NestedMessageContent(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        public NestedMessageContent(PkiMessage msg)
+            : base(msg)
+        {
+        }
+
+        public NestedMessageContent(PkiMessage[] msgs)
+            : base(msgs)
+        {
+        }
+
+        public NestedMessageContent(Asn1Sequence seq)
+            : base(seq)
+        {
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/OobCert.cs b/crypto/src/asn1/cmp/OobCert.cs
new file mode 100644
index 000000000..82d5afe55
--- /dev/null
+++ b/crypto/src/asn1/cmp/OobCert.cs
@@ -0,0 +1,68 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * OOBCert ::= CMPCertificate
+     */
+    public class OobCert
+        : CmpCertificate
+    {
+        public static new OobCert GetInstance(object obj)
+        {
+            if (obj == null)
+                return null;
+
+            if (obj is OobCert oobCert)
+                return oobCert;
+
+            if (obj is CmpCertificate cmpCertificate)
+                return GetInstance(cmpCertificate.GetEncoded());
+
+            if (obj is byte[] bs)
+            {
+                try
+                {
+                    obj = Asn1Object.FromByteArray(bs);
+                }
+                catch (IOException)
+                {
+                    throw new ArgumentException("Invalid encoding in OobCert");
+                }
+            }
+
+            if (obj is Asn1Sequence seq)
+                return new OobCert(X509CertificateStructure.GetInstance(obj));
+
+            if (obj is Asn1TaggedObject taggedObject)
+                return new OobCert(taggedObject.TagNo, taggedObject.GetObject());
+
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
+        }
+
+        public static new OobCert GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            if (taggedObject == null)
+                return null;
+
+            if (!declaredExplicit)
+                throw new ArgumentException("tag must be explicit");
+
+            return GetInstance(taggedObject.GetObject());
+        }
+
+        public OobCert(int type, Asn1Encodable otherCert)
+            : base(type, otherCert)
+        {
+        }
+
+        public OobCert(X509CertificateStructure x509v3PKCert)
+            : base(x509v3PKCert)
+        {
+        }
+    }
+}
diff --git a/crypto/src/asn1/cmp/OobCertHash.cs b/crypto/src/asn1/cmp/OobCertHash.cs
index 434939c0e..a18ff300d 100644
--- a/crypto/src/asn1/cmp/OobCertHash.cs
+++ b/crypto/src/asn1/cmp/OobCertHash.cs
@@ -6,18 +6,40 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class OobCertHash
+    /**
+     * <pre>
+     * OOBCertHash ::= SEQUENCE {
+     * hashAlg     [0] AlgorithmIdentifier     OPTIONAL,
+     * certId      [1] CertId                  OPTIONAL,
+     * hashVal         BIT STRING
+     * -- hashVal is calculated over the DER encoding of the
+     * -- self-signed certificate with the identifier certID.
+     * }
+     * </pre>
+     */
+    public class OobCertHash
 		: Asn1Encodable
 	{
-		private readonly AlgorithmIdentifier hashAlg;
-		private readonly CertId certId;
-		private readonly DerBitString  hashVal;
+        public static OobCertHash GetInstance(object obj)
+        {
+			if (obj is OobCertHash oobCertHash)
+				return oobCertHash;
+
+			if (obj != null)
+				return new OobCertHash(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly AlgorithmIdentifier m_hashAlg;
+		private readonly CertId m_certId;
+		private readonly DerBitString m_hashVal;
 
 		private OobCertHash(Asn1Sequence seq)
 		{
 			int index = seq.Count - 1;
 
-			hashVal = DerBitString.GetInstance(seq[index--]);
+			m_hashVal = DerBitString.GetInstance(seq[index--]);
 
 			for (int i = index; i >= 0; i--)
 			{
@@ -25,36 +47,21 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 				if (tObj.TagNo == 0)
 				{
-					hashAlg = AlgorithmIdentifier.GetInstance(tObj, true);
+					m_hashAlg = AlgorithmIdentifier.GetInstance(tObj, true);
 				}
 				else
 				{
-					certId = CertId.GetInstance(tObj, true);
+					m_certId = CertId.GetInstance(tObj, true);
 				}
 			}
 		}
 
-		public static OobCertHash GetInstance(object obj)
-		{
-			if (obj is OobCertHash)
-				return (OobCertHash)obj;
+		public virtual CertId CertID => m_certId;
 
-			if (obj is Asn1Sequence)
-				return new OobCertHash((Asn1Sequence)obj);
+        public virtual AlgorithmIdentifier HashAlg => m_hashAlg;
+
+		public virtual DerBitString HashVal => m_hashVal;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
-		
-		public virtual AlgorithmIdentifier HashAlg
-		{
-			get { return hashAlg; }
-		}
-		
-		public virtual CertId CertID
-		{
-			get { return certId; }
-		}
-		
 		/**
 		 * <pre>
 		 * OobCertHash ::= SEQUENCE {
@@ -70,9 +77,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		public override Asn1Object ToAsn1Object()
 		{
 			Asn1EncodableVector v = new Asn1EncodableVector();
-            v.AddOptionalTagged(true, 0, hashAlg);
-            v.AddOptionalTagged(true, 1, certId);
-			v.Add(hashVal);
+            v.AddOptionalTagged(true, 0, m_hashAlg);
+            v.AddOptionalTagged(true, 1, m_certId);
+			v.Add(m_hashVal);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/PKIBody.cs b/crypto/src/asn1/cmp/PKIBody.cs
index f17eed64d..68f63ab0b 100644
--- a/crypto/src/asn1/cmp/PKIBody.cs
+++ b/crypto/src/asn1/cmp/PKIBody.cs
@@ -6,6 +6,37 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
+    /**
+     * PKIBody ::= CHOICE {       -- message-specific body elements
+     *          ir       [0]  CertReqMessages,        --Initialization Request
+     *          ip       [1]  CertRepMessage,         --Initialization Response
+     *          cr       [2]  CertReqMessages,        --Certification Request
+     *          cp       [3]  CertRepMessage,         --Certification Response
+     *          p10cr    [4]  CertificationRequest,   --imported from [PKCS10]
+     *          popdecc  [5]  POPODecKeyChallContent, --pop Challenge
+     *          popdecr  [6]  POPODecKeyRespContent,  --pop Response
+     *          kur      [7]  CertReqMessages,        --Key Update Request
+     *          kup      [8]  CertRepMessage,         --Key Update Response
+     *          krr      [9]  CertReqMessages,        --Key Recovery Request
+     *          krp      [10] KeyRecRepContent,       --Key Recovery Response
+     *          rr       [11] RevReqContent,          --Revocation Request
+     *          rp       [12] RevRepContent,          --Revocation Response
+     *          ccr      [13] CertReqMessages,        --Cross-Cert. Request
+     *          ccp      [14] CertRepMessage,         --Cross-Cert. Response
+     *          ckuann   [15] CAKeyUpdAnnContent,     --CA Key Update Ann.
+     *          cann     [16] CertAnnContent,         --Certificate Ann.
+     *          rann     [17] RevAnnContent,          --Revocation Ann.
+     *          crlann   [18] CRLAnnContent,          --CRL Announcement
+     *          pkiconf  [19] PKIConfirmContent,      --Confirmation
+     *          nested   [20] NestedMessageContent,   --Nested Message
+     *          genm     [21] GenMsgContent,          --General Message
+     *          genp     [22] GenRepContent,          --General Response
+     *          error    [23] ErrorMsgContent,        --Error Message
+     *          certConf [24] CertConfirmContent,     --Certificate confirm
+     *          pollReq  [25] PollReqContent,         --Polling request
+     *          pollRep  [26] PollRepContent          --Polling response
+     *      }
+     */
     public class PkiBody
         : Asn1Encodable, IAsn1Choice
     {
@@ -37,24 +68,27 @@ namespace Org.BouncyCastle.Asn1.Cmp
         public const int TYPE_POLL_REQ = 25;
         public const int TYPE_POLL_REP = 26;
 
-        private int tagNo;
-        private Asn1Encodable body;
-
         public static PkiBody GetInstance(object obj)
         {
-            if (obj is PkiBody)
-                return (PkiBody)obj;
+            if (obj == null)
+                return null;
+
+            if (obj is PkiBody pkiBody)
+                return pkiBody;
 
-            if (obj is Asn1TaggedObject)
-                return new PkiBody((Asn1TaggedObject)obj);
+            if (obj is Asn1TaggedObject taggedObject)
+                return new PkiBody(taggedObject);
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
         }
 
-        private PkiBody(Asn1TaggedObject tagged)
+        private readonly int m_tagNo;
+        private readonly Asn1Encodable m_body;
+
+        private PkiBody(Asn1TaggedObject taggedObject)
         {
-            tagNo = tagged.TagNo;
-            body = GetBodyForType(tagNo, tagged.GetObject());
+            m_tagNo = taggedObject.TagNo;
+            m_body = GetBodyForType(m_tagNo, taggedObject.GetObject());
         }
 
         /**
@@ -62,88 +96,78 @@ namespace Org.BouncyCastle.Asn1.Cmp
          * @param type one of the TYPE_* constants
          * @param content message content
          */
-        public PkiBody(
-            int type,
-            Asn1Encodable content)
+        public PkiBody(int type, Asn1Encodable content)
         {
-            tagNo = type;
-            body = GetBodyForType(type, content);
+            m_tagNo = type;
+            m_body = GetBodyForType(type, content);
         }
 
-        private static Asn1Encodable GetBodyForType(
-            int type,
-            Asn1Encodable o)
+        private static Asn1Encodable GetBodyForType(int type, Asn1Encodable o)
         {
             switch (type)
             {
-                case TYPE_INIT_REQ:
-                    return CertReqMessages.GetInstance(o);
-	            case TYPE_INIT_REP:
-	                return CertRepMessage.GetInstance(o);
-                case TYPE_CERT_REQ:
-                    return CertReqMessages.GetInstance(o);
-	            case TYPE_CERT_REP:
-	                return CertRepMessage.GetInstance(o);
-	            case TYPE_P10_CERT_REQ:
-	                return CertificationRequest.GetInstance(o);
-	            case TYPE_POPO_CHALL:
-	                return PopoDecKeyChallContent.GetInstance(o);
-	            case TYPE_POPO_REP:
-	                return PopoDecKeyRespContent.GetInstance(o);
-                case TYPE_KEY_UPDATE_REQ:
-                    return CertReqMessages.GetInstance(o);
-	            case TYPE_KEY_UPDATE_REP:
-	                return CertRepMessage.GetInstance(o);
-                case TYPE_KEY_RECOVERY_REQ:
-                    return CertReqMessages.GetInstance(o);
-	            case TYPE_KEY_RECOVERY_REP:
-	                return KeyRecRepContent.GetInstance(o);
-	            case TYPE_REVOCATION_REQ:
-	                return RevReqContent.GetInstance(o);
-	            case TYPE_REVOCATION_REP:
-	                return RevRepContent.GetInstance(o);
-                case TYPE_CROSS_CERT_REQ:
-                    return CertReqMessages.GetInstance(o);
-	            case TYPE_CROSS_CERT_REP:
-	                return CertRepMessage.GetInstance(o);
-	            case TYPE_CA_KEY_UPDATE_ANN:
-	                return CAKeyUpdAnnContent.GetInstance(o);
-	            case TYPE_CERT_ANN:
-	                return CmpCertificate.GetInstance(o);
-	            case TYPE_REVOCATION_ANN:
-	                return RevAnnContent.GetInstance(o);
-	            case TYPE_CRL_ANN:
-	                return CrlAnnContent.GetInstance(o);
-	            case TYPE_CONFIRM:
-	                return PkiConfirmContent.GetInstance(o);
-                case TYPE_NESTED:
-                    return PkiMessages.GetInstance(o);
-	            case TYPE_GEN_MSG:
-	                return GenMsgContent.GetInstance(o);
-	            case TYPE_GEN_REP:
-	                return GenRepContent.GetInstance(o);
-	            case TYPE_ERROR:
-	                return ErrorMsgContent.GetInstance(o);
-	            case TYPE_CERT_CONFIRM:
-	                return CertConfirmContent.GetInstance(o);
-	            case TYPE_POLL_REQ:
-	                return PollReqContent.GetInstance(o);
-	            case TYPE_POLL_REP:
-	                return PollRepContent.GetInstance(o);
-	            default:
-	                throw new ArgumentException("unknown tag number: " + type, "type");
+            case TYPE_INIT_REQ:
+                return CertReqMessages.GetInstance(o);
+            case TYPE_INIT_REP:
+                return CertRepMessage.GetInstance(o);
+            case TYPE_CERT_REQ:
+                return CertReqMessages.GetInstance(o);
+            case TYPE_CERT_REP:
+                return CertRepMessage.GetInstance(o);
+            case TYPE_P10_CERT_REQ:
+                return CertificationRequest.GetInstance(o);
+            case TYPE_POPO_CHALL:
+                return PopoDecKeyChallContent.GetInstance(o);
+            case TYPE_POPO_REP:
+                return PopoDecKeyRespContent.GetInstance(o);
+            case TYPE_KEY_UPDATE_REQ:
+                return CertReqMessages.GetInstance(o);
+            case TYPE_KEY_UPDATE_REP:
+                return CertRepMessage.GetInstance(o);
+            case TYPE_KEY_RECOVERY_REQ:
+                return CertReqMessages.GetInstance(o);
+            case TYPE_KEY_RECOVERY_REP:
+                return KeyRecRepContent.GetInstance(o);
+            case TYPE_REVOCATION_REQ:
+                return RevReqContent.GetInstance(o);
+            case TYPE_REVOCATION_REP:
+                return RevRepContent.GetInstance(o);
+            case TYPE_CROSS_CERT_REQ:
+                return CertReqMessages.GetInstance(o);
+            case TYPE_CROSS_CERT_REP:
+                return CertRepMessage.GetInstance(o);
+            case TYPE_CA_KEY_UPDATE_ANN:
+                return CAKeyUpdAnnContent.GetInstance(o);
+            case TYPE_CERT_ANN:
+                return CmpCertificate.GetInstance(o);
+            case TYPE_REVOCATION_ANN:
+                return RevAnnContent.GetInstance(o);
+            case TYPE_CRL_ANN:
+                return CrlAnnContent.GetInstance(o);
+            case TYPE_CONFIRM:
+                return PkiConfirmContent.GetInstance(o);
+            case TYPE_NESTED:
+                return PkiMessages.GetInstance(o);
+            case TYPE_GEN_MSG:
+                return GenMsgContent.GetInstance(o);
+            case TYPE_GEN_REP:
+                return GenRepContent.GetInstance(o);
+            case TYPE_ERROR:
+                return ErrorMsgContent.GetInstance(o);
+            case TYPE_CERT_CONFIRM:
+                return CertConfirmContent.GetInstance(o);
+            case TYPE_POLL_REQ:
+                return PollReqContent.GetInstance(o);
+            case TYPE_POLL_REP:
+                return PollRepContent.GetInstance(o);
+            default:
+	            throw new ArgumentException("unknown tag number: " + type, nameof(type));
             }
         }
 
-        public virtual int Type
-        {
-            get { return tagNo; }
-        }
+        public virtual Asn1Encodable Content => m_body;
 
-        public virtual Asn1Encodable Content
-        {
-            get { return body; }
-        }
+        public virtual int Type => m_tagNo;
 
         /**
          * <pre>
@@ -181,7 +205,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
          */
         public override Asn1Object ToAsn1Object()
         {
-            return new DerTaggedObject(true, tagNo, body);
+            return new DerTaggedObject(true, m_tagNo, m_body);
         }
     }
 }
diff --git a/crypto/src/asn1/cmp/PKIConfirmContent.cs b/crypto/src/asn1/cmp/PKIConfirmContent.cs
index d154427a4..ecebb22a8 100644
--- a/crypto/src/asn1/cmp/PKIConfirmContent.cs
+++ b/crypto/src/asn1/cmp/PKIConfirmContent.cs
@@ -4,24 +4,38 @@ using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class PkiConfirmContent
+    /**
+     *  PKIConfirmContent ::= NULL
+     */
+    public class PkiConfirmContent
 		: Asn1Encodable
 	{
 		public static PkiConfirmContent GetInstance(object obj)
 		{
-			if (obj is PkiConfirmContent)
-				return (PkiConfirmContent)obj;
+			if (obj == null)
+				return null;
 
-			if (obj is Asn1Null)
-				return new PkiConfirmContent();
+			if (obj is PkiConfirmContent pkiConfirmContent)
+				return pkiConfirmContent;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
+			if (obj is Asn1Null asn1Null)
+				return new PkiConfirmContent(asn1Null);
 
-		public PkiConfirmContent()
-		{
+            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), nameof(obj));
 		}
 
+        private readonly Asn1Null m_val;
+
+        public PkiConfirmContent()
+            : this(DerNull.Instance)
+        {
+        }
+
+        private PkiConfirmContent(Asn1Null val)
+        {
+            m_val = val;
+        }
+
 		/**
 		 * <pre>
 		 * PkiConfirmContent ::= NULL
@@ -30,7 +44,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return DerNull.Instance;
+			return m_val;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/PKIFailureInfo.cs b/crypto/src/asn1/cmp/PKIFailureInfo.cs
index 75a3ff0d7..fd37665b9 100644
--- a/crypto/src/asn1/cmp/PKIFailureInfo.cs
+++ b/crypto/src/asn1/cmp/PKIFailureInfo.cs
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
      * certRevoked         (10),
      * certConfirmed       (11),
      * wrongIntegrity      (12),
-     * badRecipientNonce   (13), 
+     * badRecipientNonce   (13),
      * timeNotAvailable    (14),
      *   -- the TSA's time source is not available
      * unacceptedPolicy    (15),
@@ -37,13 +37,13 @@ namespace Org.BouncyCastle.Asn1.Cmp
      * transactionIdInUse  (21),
      * unsupportedVersion  (22),
      * notAuthorized       (23),
-     * systemUnavail       (24),    
+     * systemUnavail       (24),
      * systemFailure       (25),
      *   -- the request cannot be handled due to system failure
-     * duplicateCertReq    (26) 
+     * duplicateCertReq    (26)
      * </pre>
      */
-	public class PkiFailureInfo
+    public class PkiFailureInfo
 		: DerBitString
 	{
         public const int BadAlg               = (1 << 7); // unrecognized or unsupported Algorithm Identifier
diff --git a/crypto/src/asn1/cmp/PKIFreeText.cs b/crypto/src/asn1/cmp/PKIFreeText.cs
index 006930320..f3a4b8a81 100644
--- a/crypto/src/asn1/cmp/PKIFreeText.cs
+++ b/crypto/src/asn1/cmp/PKIFreeText.cs
@@ -1,61 +1,66 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class PkiFreeText
 		: Asn1Encodable
 	{
-		internal Asn1Sequence strings;
-
-		public static PkiFreeText GetInstance(
-			Asn1TaggedObject	obj,
-			bool				isExplicit)
+		public static PkiFreeText GetInstance(object obj)
 		{
-			return GetInstance(Asn1Sequence.GetInstance(obj, isExplicit));
+			if (obj is PkiFreeText pkiFreeText)
+				return pkiFreeText;
+
+			if (obj != null)
+                return new PkiFreeText(Asn1Sequence.GetInstance(obj));
+
+            return null;
 		}
 
-		public static PkiFreeText GetInstance(
-			object obj)
+        public static PkiFreeText GetInstance(Asn1TaggedObject taggedObject, bool declaredExplicit)
+        {
+            return GetInstance(Asn1Sequence.GetInstance(taggedObject, declaredExplicit));
+        }
+
+        internal Asn1Sequence m_strings;
+
+        internal PkiFreeText(Asn1Sequence seq)
 		{
-			if (obj is PkiFreeText)
-			{
-				return (PkiFreeText)obj;
-			}
-			else if (obj is Asn1Sequence)
+			foreach (var element in seq)
 			{
-				return new PkiFreeText((Asn1Sequence)obj);
+				if (!(element is DerUtf8String))
+					throw new ArgumentException("attempt to insert non UTF8 STRING into PkiFreeText");
 			}
 
-            throw new ArgumentException("Unknown object in factory: " + Platform.GetTypeName(obj), "obj");
+			m_strings = seq;
 		}
 
-		public PkiFreeText(
-			Asn1Sequence seq)
+		public PkiFreeText(DerUtf8String p)
 		{
-			foreach (object o in seq)
-			{
-				if (!(o is DerUtf8String))
-				{
-					throw new ArgumentException("attempt to insert non UTF8 STRING into PkiFreeText");
-				}
-			}
+			m_strings = new DerSequence(p);
+		}
 
-			this.strings = seq;
+		public PkiFreeText(string p)
+			: this(new DerUtf8String(p))
+		{
 		}
 
-		public PkiFreeText(
-			DerUtf8String p)
+		public PkiFreeText(DerUtf8String[] strs)
 		{
-			strings = new DerSequence(p);
+			m_strings = new DerSequence(strs);
 		}
 
-		public int Count
+		public PkiFreeText(string[] strs)
 		{
-			get { return strings.Count; }
+			Asn1EncodableVector v = new Asn1EncodableVector(strs.Length);
+			for (int i = 0; i < strs.Length; i++)
+			{
+				v.Add(new DerUtf8String(strs[i]));
+			}
+			m_strings = new DerSequence(v);
 		}
 
+		public virtual int Count => m_strings.Count;
+
 		/**
 		 * Return the UTF8STRING at index.
 		 *
@@ -64,7 +69,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public DerUtf8String this[int index]
 		{
-			get { return (DerUtf8String) strings[index]; }
+			get { return (DerUtf8String)m_strings[index]; }
 		}
 
 		/**
@@ -74,7 +79,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return strings;
+			return m_strings;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/PKIHeader.cs b/crypto/src/asn1/cmp/PKIHeader.cs
index 7b6296279..7ed914e6a 100644
--- a/crypto/src/asn1/cmp/PKIHeader.cs
+++ b/crypto/src/asn1/cmp/PKIHeader.cs
@@ -19,7 +19,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
         private readonly DerInteger pvno;
         private readonly GeneralName sender;
         private readonly GeneralName recipient;
-        private readonly DerGeneralizedTime messageTime;
+        private readonly Asn1GeneralizedTime messageTime;
         private readonly AlgorithmIdentifier protectionAlg;
         private readonly Asn1OctetString senderKID;       // KeyIdentifier
         private readonly Asn1OctetString recipKID;        // KeyIdentifier
@@ -41,35 +41,35 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
                 switch (tObj.TagNo)
                 {
-                    case 0:
-                        messageTime = DerGeneralizedTime.GetInstance(tObj, true);
-                        break;
-                    case 1:
-                        protectionAlg = AlgorithmIdentifier.GetInstance(tObj, true);
-                        break;
-                    case 2:
-                        senderKID = Asn1OctetString.GetInstance(tObj, true);
-                        break;
-                    case 3:
-                        recipKID = Asn1OctetString.GetInstance(tObj, true);
-                        break;
-                    case 4:
-                        transactionID = Asn1OctetString.GetInstance(tObj, true);
-                        break;
-                    case 5:
-                        senderNonce = Asn1OctetString.GetInstance(tObj, true);
-                        break;
-                    case 6:
-                        recipNonce = Asn1OctetString.GetInstance(tObj, true);
-                        break;
-                    case 7:
-                        freeText = PkiFreeText.GetInstance(tObj, true);
-                        break;
-                    case 8:
-                        generalInfo = Asn1Sequence.GetInstance(tObj, true);
-                        break;
-                    default:
-                        throw new ArgumentException("unknown tag number: " + tObj.TagNo, "seq");
+                case 0:
+                    messageTime = Asn1GeneralizedTime.GetInstance(tObj, true);
+                    break;
+                case 1:
+                    protectionAlg = AlgorithmIdentifier.GetInstance(tObj, true);
+                    break;
+                case 2:
+                    senderKID = Asn1OctetString.GetInstance(tObj, true);
+                    break;
+                case 3:
+                    recipKID = Asn1OctetString.GetInstance(tObj, true);
+                    break;
+                case 4:
+                    transactionID = Asn1OctetString.GetInstance(tObj, true);
+                    break;
+                case 5:
+                    senderNonce = Asn1OctetString.GetInstance(tObj, true);
+                    break;
+                case 6:
+                    recipNonce = Asn1OctetString.GetInstance(tObj, true);
+                    break;
+                case 7:
+                    freeText = PkiFreeText.GetInstance(tObj, true);
+                    break;
+                case 8:
+                    generalInfo = Asn1Sequence.GetInstance(tObj, true);
+                    break;
+                default:
+                    throw new ArgumentException("unknown tag number: " + tObj.TagNo, nameof(seq));
                 }
             }
         }
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
             get { return recipient; }
         }
 
-        public virtual DerGeneralizedTime MessageTime
+        public virtual Asn1GeneralizedTime MessageTime
         {
             get { return messageTime; }
         }
diff --git a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
index d771dda4c..cbefc73b8 100644
--- a/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
+++ b/crypto/src/asn1/cmp/PKIHeaderBuilder.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		private DerInteger pvno;
 		private GeneralName sender;
 		private GeneralName recipient;
-		private DerGeneralizedTime messageTime;
+		private Asn1GeneralizedTime messageTime;
 		private AlgorithmIdentifier protectionAlg;
 		private Asn1OctetString senderKID;       // KeyIdentifier
 		private Asn1OctetString recipKID;        // KeyIdentifier
@@ -37,7 +37,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 			this.recipient = recipient;
 		}
 
-		public virtual PkiHeaderBuilder SetMessageTime(DerGeneralizedTime time)
+		public virtual PkiHeaderBuilder SetMessageTime(Asn1GeneralizedTime time)
 		{
 			messageTime = time;
 			return this;
diff --git a/crypto/src/asn1/cmp/PKIMessages.cs b/crypto/src/asn1/cmp/PKIMessages.cs
index eb01e544a..0008f476a 100644
--- a/crypto/src/asn1/cmp/PKIMessages.cs
+++ b/crypto/src/asn1/cmp/PKIMessages.cs
@@ -9,7 +9,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
     {
         private Asn1Sequence content;
 
-        private PkiMessages(Asn1Sequence seq)
+        internal PkiMessages(Asn1Sequence seq)
         {
             content = seq;
         }
diff --git a/crypto/src/asn1/cmp/PbmParameter.cs b/crypto/src/asn1/cmp/PbmParameter.cs
index 206b89ba1..f4b702ed5 100644
--- a/crypto/src/asn1/cmp/PbmParameter.cs
+++ b/crypto/src/asn1/cmp/PbmParameter.cs
@@ -1,77 +1,74 @@
 using System;
 
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
+    /**
+     *  PBMParameter ::= SEQUENCE {
+     *          salt                OCTET STRING,
+     *          -- note:  implementations MAY wish to limit acceptable sizes
+     *          -- of this string to values appropriate for their environment
+     *          -- in order to reduce the risk of denial-of-service attacks
+     *          owf                 AlgorithmIdentifier,
+     *          -- AlgId for a One-Way Function (SHA-1 recommended)
+     *          iterationCount      INTEGER,
+     *          -- number of times the OWF is applied
+     *          -- note:  implementations MAY wish to limit acceptable sizes
+     *          -- of this integer to values appropriate for their environment
+     *          -- in order to reduce the risk of denial-of-service attacks
+     *          mac                 AlgorithmIdentifier
+     *          -- the MAC AlgId (e.g., DES-MAC, Triple-DES-MAC [PKCS11],
+     *      }   -- or HMAC [RFC2104, RFC2202])
+     */
     public class PbmParameter
         : Asn1Encodable
     {
-        private Asn1OctetString salt;
-        private AlgorithmIdentifier owf;
-        private DerInteger iterationCount;
-        private AlgorithmIdentifier mac;
-
-        private PbmParameter(Asn1Sequence seq)
-        {
-            salt = Asn1OctetString.GetInstance(seq[0]);
-            owf = AlgorithmIdentifier.GetInstance(seq[1]);
-            iterationCount = DerInteger.GetInstance(seq[2]);
-            mac = AlgorithmIdentifier.GetInstance(seq[3]);
-        }
-
         public static PbmParameter GetInstance(object obj)
         {
-            if (obj is PbmParameter)
-                return (PbmParameter)obj;
+            if (obj is PbmParameter pbmParameter)
+                return pbmParameter;
 
-            if (obj is Asn1Sequence)
-                return new PbmParameter((Asn1Sequence)obj);
+            if (obj != null)
+                return new PbmParameter(Asn1Sequence.GetInstance(obj));
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+            return null;
         }
 
-        public PbmParameter(
-            byte[] salt,
-            AlgorithmIdentifier owf,
-            int iterationCount,
-            AlgorithmIdentifier mac)
-            : this(new DerOctetString(salt), owf, new DerInteger(iterationCount), mac)
-        {
-        }
+        private readonly Asn1OctetString m_salt;
+        private readonly AlgorithmIdentifier m_owf;
+        private readonly DerInteger m_iterationCount;
+        private readonly AlgorithmIdentifier m_mac;
 
-        public PbmParameter(
-            Asn1OctetString salt,
-            AlgorithmIdentifier owf,
-            DerInteger iterationCount,
-            AlgorithmIdentifier mac)
+        private PbmParameter(Asn1Sequence seq)
         {
-            this.salt = salt;
-            this.owf = owf;
-            this.iterationCount = iterationCount;
-            this.mac = mac;
+            m_salt = Asn1OctetString.GetInstance(seq[0]);
+            m_owf = AlgorithmIdentifier.GetInstance(seq[1]);
+            m_iterationCount = DerInteger.GetInstance(seq[2]);
+            m_mac = AlgorithmIdentifier.GetInstance(seq[3]);
         }
 
-        public virtual Asn1OctetString Salt
+        public PbmParameter(byte[] salt, AlgorithmIdentifier owf, int iterationCount, AlgorithmIdentifier mac)
+            : this(new DerOctetString(salt), owf, new DerInteger(iterationCount), mac)
         {
-            get { return salt; }
         }
 
-        public virtual AlgorithmIdentifier Owf
+        public PbmParameter(Asn1OctetString salt, AlgorithmIdentifier owf, DerInteger iterationCount,
+            AlgorithmIdentifier mac)
         {
-            get { return owf; }
+            m_salt = salt;
+            m_owf = owf;
+            m_iterationCount = iterationCount;
+            m_mac = mac;
         }
 
-        public virtual DerInteger IterationCount
-        {
-            get { return iterationCount; }
-        }
+        public virtual DerInteger IterationCount => m_iterationCount;
 
-        public virtual AlgorithmIdentifier Mac
-        {
-            get { return mac; }
-        }
+        public virtual AlgorithmIdentifier Mac => m_mac;
+
+        public virtual AlgorithmIdentifier Owf => m_owf;
+
+        public virtual Asn1OctetString Salt => m_salt;
 
         /**
          * <pre>
@@ -95,7 +92,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
          */
         public override Asn1Object ToAsn1Object()
         {
-            return new DerSequence(salt, owf, iterationCount, mac);
+            return new DerSequence(m_salt, m_owf, m_iterationCount, m_mac);
         }
     }
 }
diff --git a/crypto/src/asn1/cmp/PollRepContent.cs b/crypto/src/asn1/cmp/PollRepContent.cs
index ff75d7d6d..15f153a5d 100644
--- a/crypto/src/asn1/cmp/PollRepContent.cs
+++ b/crypto/src/asn1/cmp/PollRepContent.cs
@@ -1,71 +1,69 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class PollRepContent
+    /**
+     * PollRepContent ::= SEQUENCE OF SEQUENCE {
+     * certReqId    INTEGER,
+     * checkAfter   INTEGER,  -- time in seconds
+     * reason       PKIFreeText OPTIONAL }
+     */
+    public class PollRepContent
 		: Asn1Encodable
 	{
-		private readonly DerInteger certReqId;
-		private readonly DerInteger checkAfter;
-		private readonly PkiFreeText reason;
+        public static PollRepContent GetInstance(object obj)
+        {
+			if (obj is PollRepContent pollRepContent)
+				return pollRepContent;
+
+			if (obj != null)
+				return new PollRepContent(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly DerInteger[] m_certReqID;
+		private readonly DerInteger[] m_checkAfter;
+		private readonly PkiFreeText[] m_reason;
 
 		private PollRepContent(Asn1Sequence seq)
 		{
-			certReqId = DerInteger.GetInstance(seq[0]);
-			checkAfter = DerInteger.GetInstance(seq[1]);
+			int count = seq.Count;
+			m_certReqID = new DerInteger[count];
+			m_checkAfter = new DerInteger[count];
+			m_reason = new PkiFreeText[count];
 
-			if (seq.Count > 2)
+			for (int i = 0; i != count; i++)
 			{
-				reason = PkiFreeText.GetInstance(seq[2]);
-			}
-		}
-
-		public static PollRepContent GetInstance(object obj)
-		{
-			if (obj is PollRepContent)
-				return (PollRepContent)obj;
+				Asn1Sequence s = Asn1Sequence.GetInstance(seq[i]);
 
-			if (obj is Asn1Sequence)
-				return new PollRepContent((Asn1Sequence)obj);
+				m_certReqID[i] = DerInteger.GetInstance(s[0]);
+				m_checkAfter[i] = DerInteger.GetInstance(s[1]);
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+				if (s.Count > 2)
+				{
+					m_reason[i] = PkiFreeText.GetInstance(s[2]);
+				}
+			}
 		}
 
-	    public PollRepContent(
-	        DerInteger certReqId,
-	        DerInteger checkAfter)
+	    public PollRepContent(DerInteger certReqID, DerInteger checkAfter)
+			: this(certReqID, checkAfter, null)
 	    {
-	        this.certReqId = certReqId;
-	        this.checkAfter = checkAfter;
-	        this.reason = null;
 	    }
 
-        public PollRepContent(
-	        DerInteger certReqId,
-	        DerInteger checkAfter,
-	        PkiFreeText reason)
+        public PollRepContent(DerInteger certReqID, DerInteger checkAfter, PkiFreeText reason)
 	    {
-	        this.certReqId = certReqId;
-	        this.checkAfter = checkAfter;
-	        this.reason = reason;
-	    }
+            m_certReqID = new DerInteger[1]{ certReqID };
+            m_checkAfter = new DerInteger[1]{ checkAfter };
+            m_reason = new PkiFreeText[1]{ reason };
+        }
 
-		public virtual DerInteger CertReqID
-		{
-			get { return certReqId; }
-		}
+        public virtual int Count => m_certReqID.Length;
 
-		public virtual DerInteger CheckAfter
-		{
-			get { return checkAfter; }
-		}
+        public virtual DerInteger GetCertReqID(int index) => m_certReqID[index];
 
-		public virtual PkiFreeText Reason
-		{
-			get { return reason; }
-		}
+		public virtual DerInteger GetCheckAfter(int index) => m_checkAfter[index];
+
+		public virtual PkiFreeText GetReason(int index) => m_reason[index];
 
 		/**
 		 * <pre>
@@ -79,9 +77,20 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(certReqId, checkAfter);
-			v.AddOptional(reason);
-			return new DerSequence(v);
+			Asn1EncodableVector outer = new Asn1EncodableVector(m_certReqID.Length);
+
+			for (int i = 0; i != m_certReqID.Length; i++)
+			{
+				Asn1EncodableVector v = new Asn1EncodableVector(3);
+
+				v.Add(m_certReqID[i]);
+				v.Add(m_checkAfter[i]);
+				v.AddOptional(m_reason[i]);
+
+				outer.Add(new DerSequence(v));
+			}
+
+			return new DerSequence(outer);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/PollReqContent.cs b/crypto/src/asn1/cmp/PollReqContent.cs
index dd9b0c352..80a39348a 100644
--- a/crypto/src/asn1/cmp/PollReqContent.cs
+++ b/crypto/src/asn1/cmp/PollReqContent.cs
@@ -1,51 +1,91 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class PollReqContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
+        public static PollReqContent GetInstance(object obj)
+        {
+			if (obj is PollReqContent pollReqContent)
+				return pollReqContent;
+
+			if (obj != null)
+				return new PollReqContent(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly Asn1Sequence m_content;
 
 		private PollReqContent(Asn1Sequence seq)
 		{
-			content = seq;
+			m_content = seq;
 		}
 
-		public static PollReqContent GetInstance(object obj)
+		/**
+		 * Create a pollReqContent for a single certReqId.
+		 *
+		 * @param certReqId the certificate request ID.
+		 */
+		public PollReqContent(DerInteger certReqId)
+			: this(new DerSequence(new DerSequence(certReqId)))
 		{
-			if (obj is PollReqContent)
-				return (PollReqContent)obj;
+		}
 
-			if (obj is Asn1Sequence)
-				return new PollReqContent((Asn1Sequence)obj);
+		/**
+		 * Create a pollReqContent for a multiple certReqIds.
+		 *
+		 * @param certReqIds the certificate request IDs.
+		 */
+		public PollReqContent(DerInteger[] certReqIds)
+			: this(new DerSequence(IntsToSequence(certReqIds)))
+		{
+		}
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+		/**
+		 * Create a pollReqContent for a single certReqId.
+		 *
+		 * @param certReqId the certificate request ID.
+		 */
+		public PollReqContent(BigInteger certReqId)
+			: this(new DerInteger(certReqId))
+		{
 		}
 
-		public virtual DerInteger[][] GetCertReqIDs()
+		/**
+		 * Create a pollReqContent for a multiple certReqIds.
+		 *
+		 * @param certReqIds the certificate request IDs.
+		 */
+		public PollReqContent(BigInteger[] certReqIds)
+			: this(IntsToAsn1(certReqIds))
 		{
-			DerInteger[][] result = new DerInteger[content.Count][];
-			for (int i = 0; i != result.Length; ++i)
-			{
-				result[i] = SequenceToDerIntegerArray((Asn1Sequence)content[i]);
-			}
-			return result;
 		}
 
-		private static DerInteger[] SequenceToDerIntegerArray(Asn1Sequence seq)
+		public virtual DerInteger[][] GetCertReqIDs()
 		{
-			DerInteger[] result = new DerInteger[seq.Count];
+			DerInteger[][] result = new DerInteger[m_content.Count][];
 			for (int i = 0; i != result.Length; ++i)
 			{
-				result[i] = DerInteger.GetInstance(seq[i]);
+				result[i] = SequenceToDerIntegerArray((Asn1Sequence)m_content[i]);
 			}
 			return result;
 		}
 
-		/**
+        public virtual BigInteger[] GetCertReqIDValues()
+        {
+            BigInteger[] result = new BigInteger[m_content.Count];
+
+            for (int i = 0; i != result.Length; i++)
+            {
+                result[i] = DerInteger.GetInstance(Asn1Sequence.GetInstance(m_content[i])[0]).Value;
+            }
+
+            return result;
+        }
+
+        /**
 		 * <pre>
 		 * PollReqContent ::= SEQUENCE OF SEQUENCE {
 		 *                        certReqId              INTEGER
@@ -53,9 +93,38 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 * </pre>
 		 * @return a basic ASN.1 object representation.
 		 */
-		public override Asn1Object ToAsn1Object()
+        public override Asn1Object ToAsn1Object()
+		{
+			return m_content;
+		}
+
+		private static DerInteger[] SequenceToDerIntegerArray(Asn1Sequence seq)
+		{
+			return seq.MapElements(DerInteger.GetInstance);
+		}
+
+		private static DerSequence[] IntsToSequence(DerInteger[] ids)
 		{
-			return content;
+			DerSequence[] result = new DerSequence[ids.Length];
+
+			for (int i = 0; i != result.Length; i++)
+			{
+				result[i] = new DerSequence(ids[i]);
+			}
+
+			return result;
+		}
+
+		private static DerInteger[] IntsToAsn1(BigInteger[] ids)
+		{
+			DerInteger[] result = new DerInteger[ids.Length];
+
+			for (int i = 0; i != result.Length; i++)
+			{
+				result[i] = new DerInteger(ids[i]);
+			}
+
+			return result;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs b/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs
index 03a13a5d5..0bd1597c8 100644
--- a/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs
+++ b/crypto/src/asn1/cmp/PopoDecKeyChallContent.cs
@@ -1,38 +1,31 @@
 using System;
 
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class PopoDecKeyChallContent
 	    : Asn1Encodable
 	{
-	    private readonly Asn1Sequence content;
+        public static PopoDecKeyChallContent GetInstance(object obj)
+        {
+			if (obj is PopoDecKeyChallContent popoDecKeyChallContent)
+				return popoDecKeyChallContent;
 
-	    private PopoDecKeyChallContent(Asn1Sequence seq)
-	    {
-	        content = seq;
-	    }
+            if (obj != null)
+                return new PopoDecKeyChallContent(Asn1Sequence.GetInstance(obj));
 
-	    public static PopoDecKeyChallContent GetInstance(object obj)
-	    {
-	        if (obj is PopoDecKeyChallContent)
-	            return (PopoDecKeyChallContent)obj;
+            return null;
+        }
 
-			if (obj is Asn1Sequence)
-	            return new PopoDecKeyChallContent((Asn1Sequence)obj);
+        private readonly Asn1Sequence m_content;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+	    private PopoDecKeyChallContent(Asn1Sequence seq)
+	    {
+	        m_content = seq;
 	    }
 
 	    public virtual Challenge[] ToChallengeArray()
 	    {
-	        Challenge[] result = new Challenge[content.Count];
-	        for (int i = 0; i != result.Length; ++i)
-	        {
-	            result[i] = Challenge.GetInstance(content[i]);
-	        }
-	        return result;
+			return m_content.MapElements(Challenge.GetInstance);
 	    }
 
 	    /**
@@ -43,7 +36,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 	     */
 	    public override Asn1Object ToAsn1Object()
 	    {
-	        return content;
+	        return m_content;
 	    }
 	}
 }
diff --git a/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs b/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs
index 73f59b7c1..77d720271 100644
--- a/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs
+++ b/crypto/src/asn1/cmp/PopoDecKeyRespContent.cs
@@ -1,38 +1,29 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class PopoDecKeyRespContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
+        public static PopoDecKeyRespContent GetInstance(object obj)
+        {
+			if (obj is PopoDecKeyRespContent popoDecKeyRespContent)
+				return popoDecKeyRespContent;
 
-		private PopoDecKeyRespContent(Asn1Sequence seq)
-		{
-			content = seq;
-		}
+			if (obj != null)
+				return new PopoDecKeyRespContent(Asn1Sequence.GetInstance(obj));
 
-		public static PopoDecKeyRespContent GetInstance(object obj)
-		{
-			if (obj is PopoDecKeyRespContent)
-				return (PopoDecKeyRespContent)obj;
+			return null;
+        }
 
-			if (obj is Asn1Sequence)
-				return new PopoDecKeyRespContent((Asn1Sequence)obj);
+        private readonly Asn1Sequence m_content;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+		private PopoDecKeyRespContent(Asn1Sequence seq)
+		{
+			m_content = seq;
 		}
 
-		public virtual DerInteger[] ToDerIntegerArray()
+		public virtual DerInteger[] ToIntegerArray()
 		{
-			DerInteger[] result = new DerInteger[content.Count];
-			for (int i = 0; i != result.Length; ++i)
-			{
-				result[i] = DerInteger.GetInstance(content[i]);
-			}
-			return result;
+			return m_content.MapElements(DerInteger.GetInstance);
 		}
 
 		/**
@@ -43,7 +34,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return content;
+			return m_content;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/ProtectedPart.cs b/crypto/src/asn1/cmp/ProtectedPart.cs
index ed90708f9..fc83ac6c6 100644
--- a/crypto/src/asn1/cmp/ProtectedPart.cs
+++ b/crypto/src/asn1/cmp/ProtectedPart.cs
@@ -1,47 +1,37 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
-
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class ProtectedPart
 		: Asn1Encodable
 	{
-		private readonly PkiHeader header;
-		private readonly PkiBody body;
-		
-		private ProtectedPart(Asn1Sequence seq)
-		{
-			header = PkiHeader.GetInstance(seq[0]);
-			body = PkiBody.GetInstance(seq[1]);
-		}
+        public static ProtectedPart GetInstance(object obj)
+        {
+			if (obj is ProtectedPart protectedPart)
+				return protectedPart;
 
-		public static ProtectedPart GetInstance(object obj)
-		{
-			if (obj is ProtectedPart)
-				return (ProtectedPart)obj;
+			if (obj != null)
+				return new ProtectedPart(Asn1Sequence.GetInstance(obj));
 
-			if (obj is Asn1Sequence)
-				return new ProtectedPart((Asn1Sequence)obj);
+			return null;
+        }
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
+        private readonly PkiHeader m_header;
+		private readonly PkiBody m_body;
 
-		public ProtectedPart(PkiHeader header, PkiBody body)
+		private ProtectedPart(Asn1Sequence seq)
 		{
-			this.header = header;
-			this.body = body;
+			m_header = PkiHeader.GetInstance(seq[0]);
+			m_body = PkiBody.GetInstance(seq[1]);
 		}
 
-		public virtual PkiHeader Header
+		public ProtectedPart(PkiHeader header, PkiBody body)
 		{
-			get { return header; }
+			m_header = header;
+			m_body = body;
 		}
 
-		public virtual PkiBody Body
-		{
-			get { return body; }
-		}
+		public virtual PkiHeader Header => m_header;
+
+		public virtual PkiBody Body => m_body;
 
 		/**
 		 * <pre>
@@ -54,7 +44,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return new DerSequence(header, body);
+			return new DerSequence(m_header, m_body);
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/RevAnnContent.cs b/crypto/src/asn1/cmp/RevAnnContent.cs
index d5d42625c..cdd26c39f 100644
--- a/crypto/src/asn1/cmp/RevAnnContent.cs
+++ b/crypto/src/asn1/cmp/RevAnnContent.cs
@@ -1,68 +1,66 @@
-using System;
-
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class RevAnnContent
 		: Asn1Encodable
 	{
-		private readonly PkiStatusEncodable status;
-		private readonly CertId certId;
-		private readonly DerGeneralizedTime willBeRevokedAt;
-		private readonly DerGeneralizedTime badSinceDate;
-		private readonly X509Extensions crlDetails;
+        public static RevAnnContent GetInstance(object obj)
+        {
+			if (obj is RevAnnContent revAnnContent)
+				return revAnnContent;
 
-		private RevAnnContent(Asn1Sequence seq)
-		{
-			status = PkiStatusEncodable.GetInstance(seq[0]);
-			certId = CertId.GetInstance(seq[1]);
-			willBeRevokedAt = DerGeneralizedTime.GetInstance(seq[2]);
-			badSinceDate = DerGeneralizedTime.GetInstance(seq[3]);
+			if (obj != null)
+				return new RevAnnContent(Asn1Sequence.GetInstance(obj));
 
-			if (seq.Count > 4)
-			{
-				crlDetails = X509Extensions.GetInstance(seq[4]);
-			}
-		}
+			return null;
+        }
 
-		public static RevAnnContent GetInstance(object obj)
-		{
-			if (obj is RevAnnContent)
-				return (RevAnnContent)obj;
-
-			if (obj is Asn1Sequence)
-				return new RevAnnContent((Asn1Sequence)obj);
+        private readonly PkiStatusEncodable m_status;
+		private readonly CertId m_certID;
+		private readonly Asn1GeneralizedTime m_willBeRevokedAt;
+		private readonly Asn1GeneralizedTime m_badSinceDate;
+		private readonly X509Extensions m_crlDetails;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+        public RevAnnContent(PkiStatusEncodable status, CertId certID, Asn1GeneralizedTime willBeRevokedAt,
+            Asn1GeneralizedTime badSinceDate)
+            : this(status, certID, willBeRevokedAt, badSinceDate, null)
+        {
 		}
 
-		public virtual PkiStatusEncodable Status
-		{
-			get { return status; }
-		}
+        public RevAnnContent(PkiStatusEncodable status, CertId certID, Asn1GeneralizedTime willBeRevokedAt,
+            Asn1GeneralizedTime badSinceDate, X509Extensions crlDetails)
+        {
+            m_status = status;
+            m_certID = certID;
+            m_willBeRevokedAt = willBeRevokedAt;
+            m_badSinceDate = badSinceDate;
+            m_crlDetails = crlDetails;
+        }
 
-		public virtual CertId CertID
+        private RevAnnContent(Asn1Sequence seq)
 		{
-			get { return certId; }
-		}
+			m_status = PkiStatusEncodable.GetInstance(seq[0]);
+			m_certID = CertId.GetInstance(seq[1]);
+			m_willBeRevokedAt = Asn1GeneralizedTime.GetInstance(seq[2]);
+			m_badSinceDate = Asn1GeneralizedTime.GetInstance(seq[3]);
 
-		public virtual DerGeneralizedTime WillBeRevokedAt
-		{
-			get { return willBeRevokedAt; }
+			if (seq.Count > 4)
+			{
+				m_crlDetails = X509Extensions.GetInstance(seq[4]);
+			}
 		}
 
-		public virtual DerGeneralizedTime BadSinceDate
-		{
-			get { return badSinceDate; }
-		}
+		public virtual PkiStatusEncodable Status => m_status;
 
-		public virtual X509Extensions CrlDetails
-		{
-			get { return crlDetails; }
-		}
+		public virtual CertId CertID => m_certID;
+
+		public virtual Asn1GeneralizedTime WillBeRevokedAt => m_willBeRevokedAt;
+
+		public virtual Asn1GeneralizedTime BadSinceDate => m_badSinceDate;
+
+		public virtual X509Extensions CrlDetails => m_crlDetails;
 
 		/**
 		 * <pre>
@@ -79,8 +77,8 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(status, certId, willBeRevokedAt, badSinceDate);
-			v.AddOptional(crlDetails);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_status, m_certID, m_willBeRevokedAt, m_badSinceDate);
+			v.AddOptional(m_crlDetails);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/RevDetails.cs b/crypto/src/asn1/cmp/RevDetails.cs
index 7d2a65ab9..9472d7775 100644
--- a/crypto/src/asn1/cmp/RevDetails.cs
+++ b/crypto/src/asn1/cmp/RevDetails.cs
@@ -1,56 +1,61 @@
-using System;
-
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class RevDetails
+    /**
+     * <pre>
+     * RevDetails ::= SEQUENCE {
+     *          certDetails         CertTemplate,
+     *          -- allows requester to specify as much as they can about
+     *          -- the cert. for which revocation is requested
+     *          -- (e.g., for cases in which serialNumber is not available)
+     *          crlEntryDetails     Extensions       OPTIONAL
+     *          -- requested crlEntryExtensions
+     *      }
+     * </pre>
+     */
+    public class RevDetails
 		: Asn1Encodable
 	{
-		private readonly CertTemplate certDetails;
-		private readonly X509Extensions crlEntryDetails;
+        public static RevDetails GetInstance(object obj)
+        {
+			if (obj is RevDetails revDetails)
+				return revDetails;
 
-        private RevDetails(Asn1Sequence seq)
-		{
-			certDetails = CertTemplate.GetInstance(seq[0]);
-            crlEntryDetails = seq.Count <= 1
-                ?   null
-                :   X509Extensions.GetInstance(seq[1]);
-		}
+			if (obj != null)
+				return new RevDetails(Asn1Sequence.GetInstance(obj));
 
-        public static RevDetails GetInstance(object obj)
-		{
-			if (obj is RevDetails)
-				return (RevDetails)obj;
+			return null;
+        }
 
-			if (obj is Asn1Sequence)
-				return new RevDetails((Asn1Sequence)obj);
+        private readonly CertTemplate m_certDetails;
+		private readonly X509Extensions m_crlEntryDetails;
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+        private RevDetails(Asn1Sequence seq)
+		{
+			m_certDetails = CertTemplate.GetInstance(seq[0]);
+
+            if (seq.Count > 1)
+            {
+                m_crlEntryDetails = X509Extensions.GetInstance(seq[1]);
+            }
 		}
 
 		public RevDetails(CertTemplate certDetails)
-            :   this(certDetails, null)
+            : this(certDetails, null)
 		{
 		}
 
         public RevDetails(CertTemplate certDetails, X509Extensions crlEntryDetails)
 		{
-            this.certDetails = certDetails;
-            this.crlEntryDetails = crlEntryDetails;
+            m_certDetails = certDetails;
+            m_crlEntryDetails = crlEntryDetails;
 		}
 
-        public virtual CertTemplate CertDetails
-		{
-			get { return certDetails; }
-		}
+		public virtual CertTemplate CertDetails => m_certDetails;
 
-        public virtual X509Extensions CrlEntryDetails
-		{
-			get { return crlEntryDetails; }
-		}
+        public virtual X509Extensions CrlEntryDetails => m_crlEntryDetails;
 
 		/**
 		* <pre>
@@ -67,8 +72,8 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		*/
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(certDetails);
-			v.AddOptional(crlEntryDetails);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_certDetails);
+			v.AddOptional(m_crlEntryDetails);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/RevRepContent.cs b/crypto/src/asn1/cmp/RevRepContent.cs
index 4b3f82b96..841b3cf94 100644
--- a/crypto/src/asn1/cmp/RevRepContent.cs
+++ b/crypto/src/asn1/cmp/RevRepContent.cs
@@ -1,21 +1,43 @@
-using System;
-
 using Org.BouncyCastle.Asn1.Crmf;
 using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Utilities;
 
 namespace Org.BouncyCastle.Asn1.Cmp
 {
-	public class RevRepContent
+    /**
+     * <pre>
+     * RevRepContent ::= SEQUENCE {
+     *          status       SEQUENCE SIZE (1..MAX) OF PKIStatusInfo,
+     *          -- in same order as was sent in RevReqContent
+     *          revCerts [0] SEQUENCE SIZE (1..MAX) OF CertId
+     *                                              OPTIONAL,
+     *          -- IDs for which revocation was requested
+     *          -- (same order as status)
+     *          crls     [1] SEQUENCE SIZE (1..MAX) OF CertificateList OPTIONAL
+     *          -- the resulting CRLs (there may be more than one)
+     *      }
+     *</pre>
+     */
+    public class RevRepContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence status;
-		private readonly Asn1Sequence revCerts;
-		private readonly Asn1Sequence crls;
+        public static RevRepContent GetInstance(object obj)
+        {
+			if (obj is RevRepContent revRepContent)
+				return revRepContent;
+
+			if (obj != null)
+				return new RevRepContent(Asn1Sequence.GetInstance(obj));
+
+			return null;
+        }
+
+        private readonly Asn1Sequence m_status;
+		private readonly Asn1Sequence m_revCerts;
+		private readonly Asn1Sequence m_crls;
 
 		private RevRepContent(Asn1Sequence seq)
 		{
-			status = Asn1Sequence.GetInstance(seq[0]);
+			m_status = Asn1Sequence.GetInstance(seq[0]);
 
 			for (int pos = 1; pos < seq.Count; ++pos)
 			{
@@ -23,60 +45,34 @@ namespace Org.BouncyCastle.Asn1.Cmp
 
 				if (tObj.TagNo == 0)
 				{
-					revCerts = Asn1Sequence.GetInstance(tObj, true);
+					m_revCerts = Asn1Sequence.GetInstance(tObj, true);
 				}
 				else
 				{
-					crls = Asn1Sequence.GetInstance(tObj, true);
+					m_crls = Asn1Sequence.GetInstance(tObj, true);
 				}
 			}
 		}
 
-		public static RevRepContent GetInstance(object obj)
-		{
-			if (obj is RevRepContent)
-				return (RevRepContent)obj;
-
-			if (obj is Asn1Sequence)
-				return new RevRepContent((Asn1Sequence)obj);
-
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
-		}
-		
 		public virtual PkiStatusInfo[] GetStatus()
 		{
-			PkiStatusInfo[] results = new PkiStatusInfo[status.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = PkiStatusInfo.GetInstance(status[i]);
-			}
-			return results;
+			return m_status.MapElements(PkiStatusInfo.GetInstance);
 		}
 
 		public virtual CertId[] GetRevCerts()
 		{
-			if (revCerts == null)
+			if (m_revCerts == null)
 				return null;
 
-			CertId[] results = new CertId[revCerts.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = CertId.GetInstance(revCerts[i]);
-			}
-			return results;
+			return m_revCerts.MapElements(CertId.GetInstance);
 		}
 
 		public virtual CertificateList[] GetCrls()
 		{
-			if (crls == null)
+			if (m_crls == null)
 				return null;
 
-			CertificateList[] results = new CertificateList[crls.Count];
-			for (int i = 0; i != results.Length; ++i)
-			{
-				results[i] = CertificateList.GetInstance(crls[i]);
-			}
-			return results;
+			return m_crls.MapElements(CertificateList.GetInstance);
 		}
 
 		/**
@@ -95,9 +91,9 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			Asn1EncodableVector v = new Asn1EncodableVector(status);
-            v.AddOptionalTagged(true, 0, revCerts);
-            v.AddOptionalTagged(true, 1, crls);
+			Asn1EncodableVector v = new Asn1EncodableVector(m_status);
+            v.AddOptionalTagged(true, 0, m_revCerts);
+            v.AddOptionalTagged(true, 1, m_crls);
 			return new DerSequence(v);
 		}
 	}
diff --git a/crypto/src/asn1/cmp/RevRepContentBuilder.cs b/crypto/src/asn1/cmp/RevRepContentBuilder.cs
index cc17d1d4c..f23bed8b5 100644
--- a/crypto/src/asn1/cmp/RevRepContentBuilder.cs
+++ b/crypto/src/asn1/cmp/RevRepContentBuilder.cs
@@ -7,29 +7,29 @@ namespace Org.BouncyCastle.Asn1.Cmp
 {
 	public class RevRepContentBuilder
 	{
-		private readonly Asn1EncodableVector status = new Asn1EncodableVector();
-		private readonly Asn1EncodableVector revCerts = new Asn1EncodableVector();
-		private readonly Asn1EncodableVector crls = new Asn1EncodableVector();
+		private readonly Asn1EncodableVector m_status = new Asn1EncodableVector();
+		private readonly Asn1EncodableVector m_revCerts = new Asn1EncodableVector();
+		private readonly Asn1EncodableVector m_crls = new Asn1EncodableVector();
 
 		public virtual RevRepContentBuilder Add(PkiStatusInfo status)
 		{
-			this.status.Add(status);
+			m_status.Add(status);
 			return this;
 		}
 
 		public virtual RevRepContentBuilder Add(PkiStatusInfo status, CertId certId)
 		{
-			if (this.status.Count != this.revCerts.Count)
+			if (m_status.Count != m_revCerts.Count)
 				throw new InvalidOperationException("status and revCerts sequence must be in common order");
 
-			this.status.Add(status);
-			this.revCerts.Add(certId);
+			m_status.Add(status);
+			m_revCerts.Add(certId);
 			return this;
 		}
 
 		public virtual RevRepContentBuilder AddCrl(CertificateList crl)
 		{
-			this.crls.Add(crl);
+			m_crls.Add(crl);
 			return this;
 		}
 
@@ -37,16 +37,16 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		{
 			Asn1EncodableVector v = new Asn1EncodableVector();
 
-			v.Add(new DerSequence(status));
+			v.Add(new DerSequence(m_status));
 
-			if (revCerts.Count != 0)
+			if (m_revCerts.Count != 0)
 			{
-				v.Add(new DerTaggedObject(true, 0, new DerSequence(revCerts)));
+				v.Add(new DerTaggedObject(true, 0, new DerSequence(m_revCerts)));
 			}
 
-			if (crls.Count != 0)
+			if (m_crls.Count != 0)
 			{
-				v.Add(new DerTaggedObject(true, 1, new DerSequence(crls)));
+				v.Add(new DerTaggedObject(true, 1, new DerSequence(m_crls)));
 			}
 
 			return RevRepContent.GetInstance(new DerSequence(v));
diff --git a/crypto/src/asn1/cmp/RevReqContent.cs b/crypto/src/asn1/cmp/RevReqContent.cs
index 1522d3789..c390530a8 100644
--- a/crypto/src/asn1/cmp/RevReqContent.cs
+++ b/crypto/src/asn1/cmp/RevReqContent.cs
@@ -7,37 +7,37 @@ namespace Org.BouncyCastle.Asn1.Cmp
 	public class RevReqContent
 		: Asn1Encodable
 	{
-		private readonly Asn1Sequence content;
-		
-		private RevReqContent(Asn1Sequence seq)
-		{
-			content = seq;
-		}
+        public static RevReqContent GetInstance(object obj)
+        {
+			if (obj is RevReqContent revReqContent)
+				return revReqContent;
 
-		public static RevReqContent GetInstance(object obj)
-		{
-			if (obj is RevReqContent)
-				return (RevReqContent)obj;
+			if (obj != null)
+				return new RevReqContent(Asn1Sequence.GetInstance(obj));
 
-			if (obj is Asn1Sequence)
-				return new RevReqContent((Asn1Sequence)obj);
+			return null;
+        }
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+        private readonly Asn1Sequence m_content;
+
+		private RevReqContent(Asn1Sequence seq)
+		{
+			m_content = seq;
 		}
 
-		public RevReqContent(params RevDetails[] revDetails)
+        public RevReqContent(RevDetails revDetails)
+        {
+            m_content = new DerSequence(revDetails);
+        }
+
+        public RevReqContent(params RevDetails[] revDetailsArray)
 		{
-			this.content = new DerSequence(revDetails);
+			m_content = new DerSequence(revDetailsArray);
 		}
 
 		public virtual RevDetails[] ToRevDetailsArray()
 		{
-			RevDetails[] result = new RevDetails[content.Count];
-			for (int i = 0; i != result.Length; ++i)
-			{
-				result[i] = RevDetails.GetInstance(content[i]);
-			}
-			return result;
+			return m_content.MapElements(RevDetails.GetInstance);
 		}
 
 		/**
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Asn1.Cmp
 		 */
 		public override Asn1Object ToAsn1Object()
 		{
-			return content;
+			return m_content;
 		}
 	}
 }
diff --git a/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs b/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
new file mode 100644
index 000000000..696b08b94
--- /dev/null
+++ b/crypto/src/asn1/cmp/RootCaKeyUpdateContent.cs
@@ -0,0 +1,91 @@
+using System;
+
+namespace Org.BouncyCastle.Asn1.Cmp
+{
+    /**
+     * GenMsg:    {id-it 20}, RootCaCertValue | &lt; absent &gt;
+     * GenRep:    {id-it 18}, RootCaKeyUpdateContent | &lt; absent &gt;
+     * <p>
+     * RootCaCertValue ::= CMPCertificate
+     * </p><p>
+     * RootCaKeyUpdateValue ::= RootCaKeyUpdateContent
+     * </p><p>
+     * RootCaKeyUpdateContent ::= SEQUENCE {
+     * newWithNew       CMPCertificate,
+     * newWithOld   [0] CMPCertificate OPTIONAL,
+     * oldWithNew   [1] CMPCertificate OPTIONAL
+     * }
+     * </p>
+     */
+    public class RootCaKeyUpdateContent
+        : Asn1Encodable
+    {
+        public static RootCaKeyUpdateContent GetInstance(object obj)
+        {
+            if (obj is RootCaKeyUpdateContent rootCaKeyUpdateContent)
+                return rootCaKeyUpdateContent;
+
+            if (obj != null)
+                return new RootCaKeyUpdateContent(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly CmpCertificate m_newWithNew;
+        private readonly CmpCertificate m_newWithOld;
+        private readonly CmpCertificate m_oldWithNew;
+
+        public RootCaKeyUpdateContent(CmpCertificate newWithNew, CmpCertificate newWithOld, CmpCertificate oldWithNew)
+        {
+            if (newWithNew == null)
+                throw new ArgumentNullException(nameof(newWithNew));
+
+            m_newWithNew = newWithNew;
+            m_newWithOld = newWithOld;
+            m_oldWithNew = oldWithNew;
+        }
+
+        private RootCaKeyUpdateContent(Asn1Sequence seq)
+        {
+            if (seq.Count < 1 || seq.Count > 3)
+                throw new ArgumentException("expected sequence of 1 to 3 elements only");
+
+            CmpCertificate newWithNew;
+            CmpCertificate newWithOld = null;
+            CmpCertificate oldWithNew = null;
+
+            newWithNew = CmpCertificate.GetInstance(seq[0]);
+
+            for (int pos = 1; pos < seq.Count; ++pos)
+            {
+                Asn1TaggedObject ato = Asn1TaggedObject.GetInstance(seq[pos]);
+                if (ato.TagNo == 0)
+                {
+                    newWithOld = CmpCertificate.GetInstance(ato, true);
+                }
+                else if (ato.TagNo == 1)
+                {
+                    oldWithNew = CmpCertificate.GetInstance(ato, true);
+                }
+            }
+
+            m_newWithNew = newWithNew;
+            m_newWithOld = newWithOld;
+            m_oldWithNew = oldWithNew;
+        }
+
+        public virtual CmpCertificate NewWithNew => m_newWithNew;
+
+        public virtual CmpCertificate NewWithOld => m_newWithOld;
+
+        public virtual CmpCertificate OldWithNew => m_oldWithNew;
+
+        public override Asn1Object ToAsn1Object()
+        {
+            Asn1EncodableVector v = new Asn1EncodableVector(m_newWithNew);
+            v.AddOptionalTagged(true, 0, m_newWithOld);
+            v.AddOptionalTagged(true, 1, m_oldWithNew);
+            return new DerSequence(v);
+        }
+    }
+}
diff --git a/crypto/src/asn1/cms/CMSObjectIdentifiers.cs b/crypto/src/asn1/cms/CMSObjectIdentifiers.cs
index 2ad0a3c7c..6f4f8fb5e 100644
--- a/crypto/src/asn1/cms/CMSObjectIdentifiers.cs
+++ b/crypto/src/asn1/cms/CMSObjectIdentifiers.cs
@@ -13,7 +13,8 @@ namespace Org.BouncyCastle.Asn1.Cms
         public static readonly DerObjectIdentifier AuthenticatedData = PkcsObjectIdentifiers.IdCTAuthData;
         public static readonly DerObjectIdentifier CompressedData = PkcsObjectIdentifiers.IdCTCompressedData;
         public static readonly DerObjectIdentifier AuthEnvelopedData = PkcsObjectIdentifiers.IdCTAuthEnvelopedData;
-        public static readonly DerObjectIdentifier timestampedData = PkcsObjectIdentifiers.IdCTTimestampedData;
+        public static readonly DerObjectIdentifier TimestampedData = PkcsObjectIdentifiers.IdCTTimestampedData;
+        public static readonly DerObjectIdentifier ZlibCompress = PkcsObjectIdentifiers.IdAlgZlibCompress;
 
         /**
          * The other Revocation Info arc
diff --git a/crypto/src/asn1/cms/KEKIdentifier.cs b/crypto/src/asn1/cms/KEKIdentifier.cs
index a42217440..36ab94f52 100644
--- a/crypto/src/asn1/cms/KEKIdentifier.cs
+++ b/crypto/src/asn1/cms/KEKIdentifier.cs
@@ -8,12 +8,12 @@ namespace Org.BouncyCastle.Asn1.Cms
         : Asn1Encodable
     {
         private Asn1OctetString		keyIdentifier;
-        private DerGeneralizedTime	date;
+        private Asn1GeneralizedTime date;
         private OtherKeyAttribute	other;
 
 		public KekIdentifier(
             byte[]              keyIdentifier,
-            DerGeneralizedTime  date,
+            Asn1GeneralizedTime date,
             OtherKeyAttribute   other)
         {
             this.keyIdentifier = new DerOctetString(keyIdentifier);
@@ -31,9 +31,9 @@ namespace Org.BouncyCastle.Asn1.Cms
             case 1:
 				break;
             case 2:
-				if (seq[1] is DerGeneralizedTime)
+				if (seq[1] is Asn1GeneralizedTime)
 				{
-					date = (DerGeneralizedTime) seq[1];
+					date = (Asn1GeneralizedTime) seq[1];
 				}
 				else
 				{
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 				}
 				break;
             case 3:
-				date  = (DerGeneralizedTime) seq[1];
+				date  = (Asn1GeneralizedTime) seq[1];
 				other = OtherKeyAttribute.GetInstance(seq[2]);
 				break;
             default:
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 			get { return keyIdentifier; }
 		}
 
-		public DerGeneralizedTime Date
+		public Asn1GeneralizedTime Date
 		{
 			get { return date; }
 		}
diff --git a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
index 995ddab51..dea9ce09d 100644
--- a/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
+++ b/crypto/src/asn1/cms/RecipientKeyIdentifier.cs
@@ -8,12 +8,12 @@ namespace Org.BouncyCastle.Asn1.Cms
         : Asn1Encodable
     {
         private Asn1OctetString      subjectKeyIdentifier;
-        private DerGeneralizedTime   date;
+        private Asn1GeneralizedTime  date;
         private OtherKeyAttribute    other;
 
 		public RecipientKeyIdentifier(
             Asn1OctetString         subjectKeyIdentifier,
-            DerGeneralizedTime      date,
+            Asn1GeneralizedTime     date,
             OtherKeyAttribute       other)
         {
             this.subjectKeyIdentifier = subjectKeyIdentifier;
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 
 		public RecipientKeyIdentifier(
 			byte[]				subjectKeyIdentifier,
-			DerGeneralizedTime	date,
+            Asn1GeneralizedTime date,
 			OtherKeyAttribute	other)
 		{
 			this.subjectKeyIdentifier = new DerOctetString(subjectKeyIdentifier);
@@ -48,9 +48,9 @@ namespace Org.BouncyCastle.Asn1.Cms
 				case 1:
 					break;
 				case 2:
-					if (seq[1] is DerGeneralizedTime)
+					if (seq[1] is Asn1GeneralizedTime)
 					{
-						date = (DerGeneralizedTime) seq[1];
+						date = (Asn1GeneralizedTime)seq[1];
 					}
 					else
 					{
@@ -58,7 +58,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 					}
 					break;
 				case 3:
-					date  = (DerGeneralizedTime) seq[1];
+					date  = (Asn1GeneralizedTime)seq[1];
 					other = OtherKeyAttribute.GetInstance(seq[2]);
 					break;
 				default:
@@ -105,7 +105,7 @@ namespace Org.BouncyCastle.Asn1.Cms
 			get { return subjectKeyIdentifier; }
 		}
 
-		public DerGeneralizedTime Date
+		public Asn1GeneralizedTime Date
 		{
 			get { return date; }
 		}
diff --git a/crypto/src/asn1/cms/Time.cs b/crypto/src/asn1/cms/Time.cs
index 47e284e69..67c73285b 100644
--- a/crypto/src/asn1/cms/Time.cs
+++ b/crypto/src/asn1/cms/Time.cs
@@ -22,7 +22,7 @@ namespace Org.BouncyCastle.Asn1.Cms
         {
             if (time == null)
                 throw new ArgumentNullException("time");
-            if (!(time is DerUtcTime) && !(time is DerGeneralizedTime))
+            if (!(time is Asn1UtcTime) && !(time is Asn1GeneralizedTime))
                 throw new ArgumentException("unknown object passed to Time");
 
             this.time = time;
@@ -33,8 +33,7 @@ namespace Org.BouncyCastle.Asn1.Cms
          * and 2049 a UTCTime object is Generated, otherwise a GeneralizedTime
          * is used.
          */
-        public Time(
-            DateTime date)
+        public Time(DateTime date)
         {
             DateTime d = date.ToUniversalTime();
 
@@ -48,15 +47,16 @@ namespace Org.BouncyCastle.Asn1.Cms
             }
         }
 
-		public static Time GetInstance(
-            object obj)
+		public static Time GetInstance(object obj)
         {
-            if (obj == null || obj is Time)
-                return (Time)obj;
-			if (obj is DerUtcTime)
-                return new Time((DerUtcTime)obj);
-			if (obj is DerGeneralizedTime)
-                return new Time((DerGeneralizedTime)obj);
+            if (obj == null)
+                return null;
+            if (obj is Time time)
+                return time;
+			if (obj is Asn1UtcTime utcTime)
+                return new Time(utcTime);
+			if (obj is Asn1GeneralizedTime generalizedTime)
+                return new Time(generalizedTime);
 
             throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
         }
@@ -65,14 +65,10 @@ namespace Org.BouncyCastle.Asn1.Cms
         {
 			get
 			{
-				if (time is DerUtcTime)
-				{
-					return ((DerUtcTime)time).AdjustedTimeString;
-				}
-				else
-				{
-					return ((DerGeneralizedTime)time).GetTime();
-				}
+				if (time is Asn1UtcTime utcTime)
+					return utcTime.AdjustedTimeString;
+
+                return ((Asn1GeneralizedTime)time).GetTime();
 			}
         }
 
@@ -82,12 +78,10 @@ namespace Org.BouncyCastle.Asn1.Cms
 			{
 				try
 				{
-					if (time is DerUtcTime)
-					{
-						return ((DerUtcTime)time).ToAdjustedDateTime();
-					}
+					if (time is Asn1UtcTime utcTime)
+						return utcTime.ToAdjustedDateTime();
 
-					return ((DerGeneralizedTime)time).ToDateTime();
+					return ((Asn1GeneralizedTime)time).ToDateTime();
 				}
 				catch (FormatException e)
 				{
diff --git a/crypto/src/asn1/crmf/EncryptedKey.cs b/crypto/src/asn1/crmf/EncryptedKey.cs
index 850fbd219..d4ff250c5 100644
--- a/crypto/src/asn1/crmf/EncryptedKey.cs
+++ b/crypto/src/asn1/crmf/EncryptedKey.cs
@@ -1,58 +1,44 @@
-using System;
-
-using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Cms;
 
 namespace Org.BouncyCastle.Asn1.Crmf
 {
     public class EncryptedKey
         : Asn1Encodable, IAsn1Choice
     {
-        private readonly EnvelopedData envelopedData;
-        private readonly EncryptedValue encryptedValue;
-
-        public static EncryptedKey GetInstance(object o)
+        public static EncryptedKey GetInstance(object obj)
         {
-            if (o is EncryptedKey)
-            {
-                return (EncryptedKey)o;
-            }
-            else if (o is Asn1TaggedObject)
-            {
-                return new EncryptedKey(EnvelopedData.GetInstance((Asn1TaggedObject)o, false));
-            }
-            else if (o is EncryptedValue)
-            {
-                return new EncryptedKey((EncryptedValue)o);
-            }
-            else
-            {
-                return new EncryptedKey(EncryptedValue.GetInstance(o));
-            }
+            if (obj is EncryptedKey encryptedKey)
+                return encryptedKey;
+
+            if (obj is Asn1TaggedObject taggedObject)
+                return new EncryptedKey(EnvelopedData.GetInstance(taggedObject, false));
+
+            return new EncryptedKey(EncryptedValue.GetInstance(obj));
         }
 
+        private readonly EnvelopedData m_envelopedData;
+        private readonly EncryptedValue m_encryptedValue;
+
         public EncryptedKey(EnvelopedData envelopedData)
         {
-            this.envelopedData = envelopedData;
+            m_envelopedData = envelopedData;
         }
 
         public EncryptedKey(EncryptedValue encryptedValue)
         {
-            this.encryptedValue = encryptedValue;
+            m_encryptedValue = encryptedValue;
         }
 
-        public virtual bool IsEncryptedValue
-        {
-            get { return encryptedValue != null; }
-        }
+        public virtual bool IsEncryptedValue => m_encryptedValue != null;
 
         public virtual Asn1Encodable Value
         {
             get
             {
-                if (encryptedValue != null)
-                    return encryptedValue;
+                if (m_encryptedValue != null)
+                    return m_encryptedValue;
 
-                return envelopedData;
+                return m_envelopedData;
             }
         }
 
@@ -67,12 +53,10 @@ namespace Org.BouncyCastle.Asn1.Crmf
          */
         public override Asn1Object ToAsn1Object()
         {
-            if (encryptedValue != null)
-            {
-                return encryptedValue.ToAsn1Object();
-            }
+            if (m_encryptedValue != null)
+                return m_encryptedValue.ToAsn1Object();
 
-            return new DerTaggedObject(false, 0, envelopedData);
+            return new DerTaggedObject(false, 0, m_envelopedData);
         }
     }
 }
diff --git a/crypto/src/asn1/crmf/EncryptedValue.cs b/crypto/src/asn1/crmf/EncryptedValue.cs
index 7c5cf18b4..950298504 100644
--- a/crypto/src/asn1/crmf/EncryptedValue.cs
+++ b/crypto/src/asn1/crmf/EncryptedValue.cs
@@ -7,48 +7,10 @@ namespace Org.BouncyCastle.Asn1.Crmf
     public class EncryptedValue
         : Asn1Encodable
     {
-        private readonly AlgorithmIdentifier intendedAlg;
-        private readonly AlgorithmIdentifier symmAlg;
-        private readonly DerBitString encSymmKey;
-        private readonly AlgorithmIdentifier keyAlg;
-        private readonly Asn1OctetString valueHint;
-        private readonly DerBitString encValue;
-
-        private EncryptedValue(Asn1Sequence seq)
-        {
-            int index = 0;
-            while (seq[index] is Asn1TaggedObject)
-            {
-                Asn1TaggedObject tObj = (Asn1TaggedObject)seq[index];
-
-                switch (tObj.TagNo)
-                {
-                    case 0:
-                        intendedAlg = AlgorithmIdentifier.GetInstance(tObj, false);
-                        break;
-                    case 1:
-                        symmAlg = AlgorithmIdentifier.GetInstance(tObj, false);
-                        break;
-                    case 2:
-                        encSymmKey = DerBitString.GetInstance(tObj, false);
-                        break;
-                    case 3:
-                        keyAlg = AlgorithmIdentifier.GetInstance(tObj, false);
-                        break;
-                    case 4:
-                        valueHint = Asn1OctetString.GetInstance(tObj, false);
-                        break;
-                }
-                ++index;
-            }
-
-            encValue = DerBitString.GetInstance(seq[index]);
-        }
-
         public static EncryptedValue GetInstance(object obj)
         {
-            if (obj is EncryptedValue)
-                return (EncryptedValue)obj;
+            if (obj is EncryptedValue encryptedValue)
+                return encryptedValue;
 
             if (obj != null)
                 return new EncryptedValue(Asn1Sequence.GetInstance(obj));
@@ -56,59 +18,71 @@ namespace Org.BouncyCastle.Asn1.Crmf
             return null;
         }
 
-        public EncryptedValue(
-            AlgorithmIdentifier intendedAlg,
-            AlgorithmIdentifier symmAlg,
-            DerBitString encSymmKey,
-            AlgorithmIdentifier keyAlg,
-            Asn1OctetString valueHint,
-            DerBitString encValue)
+        private readonly AlgorithmIdentifier m_intendedAlg;
+        private readonly AlgorithmIdentifier m_symmAlg;
+        private readonly DerBitString m_encSymmKey;
+        private readonly AlgorithmIdentifier m_keyAlg;
+        private readonly Asn1OctetString m_valueHint;
+        private readonly DerBitString m_encValue;
+
+        private EncryptedValue(Asn1Sequence seq)
         {
-            if (encValue == null)
+            int index = 0;
+            while (seq[index] is Asn1TaggedObject tObj)
             {
-                throw new ArgumentNullException("encValue");
+                switch (tObj.TagNo)
+                {
+                case 0:
+                    m_intendedAlg = AlgorithmIdentifier.GetInstance(tObj, false);
+                    break;
+                case 1:
+                    m_symmAlg = AlgorithmIdentifier.GetInstance(tObj, false);
+                    break;
+                case 2:
+                    m_encSymmKey = DerBitString.GetInstance(tObj, false);
+                    break;
+                case 3:
+                    m_keyAlg = AlgorithmIdentifier.GetInstance(tObj, false);
+                    break;
+                case 4:
+                    m_valueHint = Asn1OctetString.GetInstance(tObj, false);
+                    break;
+                }
+                ++index;
             }
 
-            this.intendedAlg = intendedAlg;
-            this.symmAlg = symmAlg;
-            this.encSymmKey = encSymmKey;
-            this.keyAlg = keyAlg;
-            this.valueHint = valueHint;
-            this.encValue = encValue;
+            m_encValue = DerBitString.GetInstance(seq[index]);
         }
 
-        public virtual AlgorithmIdentifier IntendedAlg
+        public EncryptedValue(AlgorithmIdentifier intendedAlg, AlgorithmIdentifier symmAlg, DerBitString encSymmKey,
+            AlgorithmIdentifier keyAlg, Asn1OctetString valueHint, DerBitString encValue)
         {
-            get { return intendedAlg; }
+            if (encValue == null)
+                throw new ArgumentNullException(nameof(encValue));
+
+            m_intendedAlg = intendedAlg;
+            m_symmAlg = symmAlg;
+            m_encSymmKey = encSymmKey;
+            m_keyAlg = keyAlg;
+            m_valueHint = valueHint;
+            m_encValue = encValue;
         }
 
-        public virtual AlgorithmIdentifier SymmAlg
-        {
-            get { return symmAlg; }
-        }
+        public virtual AlgorithmIdentifier IntendedAlg => m_intendedAlg;
 
-        public virtual DerBitString EncSymmKey
-        {
-            get { return encSymmKey; }
-        }
+        public virtual AlgorithmIdentifier SymmAlg => m_symmAlg;
 
-        public virtual AlgorithmIdentifier KeyAlg
-        {
-            get { return keyAlg; }
-        }
+        public virtual DerBitString EncSymmKey => m_encSymmKey;
 
-        public virtual Asn1OctetString ValueHint
-        {
-            get { return valueHint; }
-        }
+        public virtual AlgorithmIdentifier KeyAlg => m_keyAlg;
 
-        public virtual DerBitString EncValue
-        {
-            get { return encValue; }
-        }
+        public virtual Asn1OctetString ValueHint => m_valueHint;
+
+        public virtual DerBitString EncValue => m_encValue;
 
         /**
          * <pre>
+         * (IMPLICIT TAGS)
          * EncryptedValue ::= SEQUENCE {
          *                     intendedAlg   [0] AlgorithmIdentifier  OPTIONAL,
          *                     -- the intended algorithm for which the value will be used
@@ -131,12 +105,12 @@ namespace Org.BouncyCastle.Asn1.Crmf
         public override Asn1Object ToAsn1Object()
         {
             Asn1EncodableVector v = new Asn1EncodableVector();
-            v.AddOptionalTagged(false, 0, intendedAlg);
-            v.AddOptionalTagged(false, 1, symmAlg);
-            v.AddOptionalTagged(false, 2, encSymmKey);
-            v.AddOptionalTagged(false, 3, keyAlg);
-            v.AddOptionalTagged(false, 4, valueHint);
-            v.Add(encValue);
+            v.AddOptionalTagged(false, 0, m_intendedAlg);
+            v.AddOptionalTagged(false, 1, m_symmAlg);
+            v.AddOptionalTagged(false, 2, m_encSymmKey);
+            v.AddOptionalTagged(false, 3, m_keyAlg);
+            v.AddOptionalTagged(false, 4, m_valueHint);
+            v.Add(m_encValue);
             return new DerSequence(v);
         }
     }
diff --git a/crypto/src/asn1/crmf/PKIPublicationInfo.cs b/crypto/src/asn1/crmf/PKIPublicationInfo.cs
index a7d2bc603..c855a7d28 100644
--- a/crypto/src/asn1/crmf/PKIPublicationInfo.cs
+++ b/crypto/src/asn1/crmf/PKIPublicationInfo.cs
@@ -1,48 +1,91 @@
-using System;
-
-using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Math;
 
 namespace Org.BouncyCastle.Asn1.Crmf
 {
+    /**
+     * <pre>
+     * PKIPublicationInfo ::= SEQUENCE {
+     *                  action     INTEGER {
+     *                                 dontPublish (0),
+     *                                 pleasePublish (1) },
+     *                  pubInfos  SEQUENCE SIZE (1..MAX) OF SinglePubInfo OPTIONAL }
+     * -- pubInfos MUST NOT be present if action is "dontPublish"
+     * -- (if action is "pleasePublish" and pubInfos is omitted,
+     * -- "dontCare" is assumed)
+     * </pre>
+     */
     public class PkiPublicationInfo
         : Asn1Encodable
     {
-        private readonly DerInteger action;
-        private readonly Asn1Sequence pubInfos;
+        public static readonly DerInteger DontPublish = new DerInteger(0);
+        public static readonly DerInteger PleasePublish = new DerInteger(1);
+
+        public static PkiPublicationInfo GetInstance(object obj)
+        {
+            if (obj is PkiPublicationInfo pkiPublicationInfo)
+                return pkiPublicationInfo;
+
+            if (obj != null)
+                return new PkiPublicationInfo(Asn1Sequence.GetInstance(obj));
+
+            return null;
+        }
+
+        private readonly DerInteger m_action;
+        private readonly Asn1Sequence m_pubInfos;
 
         private PkiPublicationInfo(Asn1Sequence seq)
         {
-            action = DerInteger.GetInstance(seq[0]);
-            pubInfos = Asn1Sequence.GetInstance(seq[1]);
+            m_action = DerInteger.GetInstance(seq[0]);
+            if (seq.Count > 1)
+            {
+                m_pubInfos = Asn1Sequence.GetInstance(seq[1]);
+            }
         }
 
-        public static PkiPublicationInfo GetInstance(object obj)
+        public PkiPublicationInfo(BigInteger action)
+            : this(new DerInteger(action))
         {
-            if (obj is PkiPublicationInfo)
-                return (PkiPublicationInfo)obj;
+        }
 
-            if (obj is Asn1Sequence)
-                return new PkiPublicationInfo((Asn1Sequence)obj);
+        public PkiPublicationInfo(DerInteger action)
+        {
+            m_action = action;
+        }
 
-            throw new ArgumentException("Invalid object: " + Platform.GetTypeName(obj), "obj");
+        /**
+         * Constructor with a single pubInfo, assumes pleasePublish as the action.
+         *
+         * @param pubInfo the pubInfo to be published (can be null if don't care is required).
+         */
+        public PkiPublicationInfo(SinglePubInfo pubInfo)
+            : this(pubInfo != null ? new SinglePubInfo[1]{ pubInfo } : null)
+        {
         }
 
-        public virtual DerInteger Action
+        /**
+         * Constructor with multiple pubInfo, assumes pleasePublish as the action.
+         *
+         * @param pubInfos the pubInfos to be published (can be null if don't care is required).
+         */
+        public PkiPublicationInfo(SinglePubInfo[] pubInfos)
         {
-            get { return action; }
+            m_action = PleasePublish;
+
+            if (pubInfos != null)
+            {
+                m_pubInfos = new DerSequence(pubInfos);
+            }
         }
 
+        public virtual DerInteger Action => m_action;
+
         public virtual SinglePubInfo[] GetPubInfos()
         {
-            if (pubInfos == null)
+            if (m_pubInfos == null)
                 return null;
 
-            SinglePubInfo[] results = new SinglePubInfo[pubInfos.Count];
-            for (int i = 0; i != results.Length; ++i)
-            {
-                results[i] = SinglePubInfo.GetInstance(pubInfos[i]);
-            }
-            return results;
+            return m_pubInfos.MapElements(SinglePubInfo.GetInstance);
         }
 
         /**
@@ -60,7 +103,10 @@ namespace Org.BouncyCastle.Asn1.Crmf
          */
         public override Asn1Object ToAsn1Object()
         {
-            return new DerSequence(action, pubInfos);
+            if (m_pubInfos == null)
+                return new DerSequence(m_action);
+
+            return new DerSequence(m_action, m_pubInfos);
         }
     }
 }
diff --git a/crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs b/crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs
new file mode 100644
index 000000000..e7208bab2
--- /dev/null
+++ b/crypto/src/asn1/cryptlib/CryptlibObjectIdentifiers.cs
@@ -0,0 +1,11 @@
+namespace Org.BouncyCastle.Asn1.Cryptlib
+{
+    internal class CryptlibObjectIdentifiers
+    {
+        internal static readonly DerObjectIdentifier cryptlib = new DerObjectIdentifier("1.3.6.1.4.1.3029");
+
+        internal static readonly DerObjectIdentifier ecc = cryptlib.Branch("1.5");
+
+        internal static readonly DerObjectIdentifier curvey25519 = ecc.Branch("1");
+    }
+}
diff --git a/crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs b/crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs
index e2f2c1848..5e3cb4781 100644
--- a/crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs
+++ b/crypto/src/asn1/cryptopro/CryptoProObjectIdentifiers.cs
@@ -13,7 +13,9 @@ namespace Org.BouncyCastle.Asn1.CryptoPro
 		public static readonly DerObjectIdentifier GostR3411 = new DerObjectIdentifier(GostID + ".9");
         public static readonly DerObjectIdentifier GostR3411Hmac = new DerObjectIdentifier(GostID + ".10");
 
-        public static readonly DerObjectIdentifier GostR28147Cbc = new DerObjectIdentifier(GostID + ".21");
+        public static readonly DerObjectIdentifier GostR28147Gcfb = new DerObjectIdentifier(GostID + ".21");
+        [Obsolete("Use 'GostR28147Gcfb' instead")]
+        public static readonly DerObjectIdentifier GostR28147Cbc = GostR28147Gcfb;
 
         public static readonly DerObjectIdentifier ID_Gost28147_89_CryptoPro_A_ParamSet = new DerObjectIdentifier(GostID + ".31.1");
 
@@ -47,5 +49,9 @@ namespace Org.BouncyCastle.Asn1.CryptoPro
 
 		public static readonly DerObjectIdentifier GostElSgDH3410Default = new DerObjectIdentifier(GostID + ".36.0");
         public static readonly DerObjectIdentifier GostElSgDH3410x1 = new DerObjectIdentifier(GostID + ".36.1");
+
+        public static readonly DerObjectIdentifier GostR3410x2001CryptoProESDH = new DerObjectIdentifier(GostID + ".96");
+
+        public static readonly DerObjectIdentifier GostR3410x2001DH = new DerObjectIdentifier(GostID + ".98");
     }
 }
diff --git a/crypto/src/asn1/esf/CrlIdentifier.cs b/crypto/src/asn1/esf/CrlIdentifier.cs
index a8e40c870..44c99170c 100644
--- a/crypto/src/asn1/esf/CrlIdentifier.cs
+++ b/crypto/src/asn1/esf/CrlIdentifier.cs
@@ -1,5 +1,5 @@
 using System;
-
+using System.Reflection;
 using Org.BouncyCastle.Asn1.X509;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
@@ -21,7 +21,7 @@ namespace Org.BouncyCastle.Asn1.Esf
 		: Asn1Encodable
 	{
 		private readonly X509Name	crlIssuer;
-		private readonly DerUtcTime	crlIssuedTime;
+		private readonly Asn1UtcTime crlIssuedTime;
 		private readonly DerInteger	crlNumber;
 
 		public static CrlIdentifier GetInstance(
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Asn1.Esf
 				throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
 
 			this.crlIssuer = X509Name.GetInstance(seq[0]);
-			this.crlIssuedTime = DerUtcTime.GetInstance(seq[1]);
+			this.crlIssuedTime = Asn1UtcTime.GetInstance(seq[1]);
 
 			if (seq.Count > 2)
 			{
@@ -56,31 +56,36 @@ namespace Org.BouncyCastle.Asn1.Esf
 			}
 		}
 
-		public CrlIdentifier(
-			X509Name	crlIssuer,
-			DateTime	crlIssuedTime)
-			: this(crlIssuer, crlIssuedTime, null)
+        public CrlIdentifier(X509Name crlIssuer, DateTime crlIssuedTime)
+            : this(crlIssuer, crlIssuedTime, null)
 		{
 		}
 
-		public CrlIdentifier(
-			X509Name	crlIssuer,
-			DateTime	crlIssuedTime,
-			BigInteger	crlNumber)
+		public CrlIdentifier(X509Name crlIssuer, DateTime crlIssuedTime, BigInteger crlNumber)
+			: this(crlIssuer, new Asn1UtcTime(crlIssuedTime), crlNumber)
 		{
-			if (crlIssuer == null)
-				throw new ArgumentNullException("crlIssuer");
+		}
 
-			this.crlIssuer = crlIssuer;
-			this.crlIssuedTime = new DerUtcTime(crlIssuedTime);
+        public CrlIdentifier(X509Name crlIssuer, Asn1UtcTime crlIssuedTime)
+            : this(crlIssuer, crlIssuedTime, null)
+        {
+        }
 
-			if (crlNumber != null)
-			{
-				this.crlNumber = new DerInteger(crlNumber);
-			}
-		}
+        public CrlIdentifier(X509Name crlIssuer, Asn1UtcTime crlIssuedTime, BigInteger crlNumber)
+        {
+            if (crlIssuer == null)
+                throw new ArgumentNullException(nameof(crlIssuer));
+
+            this.crlIssuer = crlIssuer;
+            this.crlIssuedTime = crlIssuedTime;
+
+            if (null != crlNumber)
+            {
+                this.crlNumber = new DerInteger(crlNumber);
+            }
+        }
 
-		public X509Name CrlIssuer
+        public X509Name CrlIssuer
 		{
 			get { return crlIssuer; }
 		}
diff --git a/crypto/src/asn1/esf/OcspIdentifier.cs b/crypto/src/asn1/esf/OcspIdentifier.cs
index e65f1cfe7..fa7069aed 100644
--- a/crypto/src/asn1/esf/OcspIdentifier.cs
+++ b/crypto/src/asn1/esf/OcspIdentifier.cs
@@ -20,7 +20,7 @@ namespace Org.BouncyCastle.Asn1.Esf
 		: Asn1Encodable
 	{
 		private readonly ResponderID		ocspResponderID;
-		private readonly DerGeneralizedTime	producedAt;
+		private readonly Asn1GeneralizedTime producedAt;
 
 		public static OcspIdentifier GetInstance(
 			object obj)
@@ -46,21 +46,30 @@ namespace Org.BouncyCastle.Asn1.Esf
 				throw new ArgumentException("Bad sequence size: " + seq.Count, "seq");
 
 			this.ocspResponderID = ResponderID.GetInstance(seq[0].ToAsn1Object());
-			this.producedAt = (DerGeneralizedTime) seq[1].ToAsn1Object();
+			this.producedAt = (Asn1GeneralizedTime)seq[1].ToAsn1Object();
 		}
 
-		public OcspIdentifier(
-			ResponderID	ocspResponderID,
-			DateTime	producedAt)
+		public OcspIdentifier(ResponderID ocspResponderID, DateTime producedAt)
 		{
 			if (ocspResponderID == null)
-				throw new ArgumentNullException();
+				throw new ArgumentNullException(nameof(ocspResponderID));
 
 			this.ocspResponderID = ocspResponderID;
-			this.producedAt = new DerGeneralizedTime(producedAt);
+			this.producedAt = new Asn1GeneralizedTime(producedAt);
 		}
 
-		public ResponderID OcspResponderID
+        public OcspIdentifier(ResponderID ocspResponderID, Asn1GeneralizedTime producedAt)
+        {
+            if (ocspResponderID == null)
+                throw new ArgumentNullException(nameof(ocspResponderID));
+            if (producedAt == null)
+                throw new ArgumentNullException(nameof(producedAt));
+
+            this.ocspResponderID = ocspResponderID;
+            this.producedAt = producedAt;
+        }
+
+        public ResponderID OcspResponderID
 		{
 			get { return ocspResponderID; }
 		}
diff --git a/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs b/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
index b82c9373d..c9c96cbda 100644
--- a/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
+++ b/crypto/src/asn1/isismtt/x509/DeclarationOfMajority.cs
@@ -64,7 +64,7 @@ namespace Org.BouncyCastle.Asn1.IsisMtt.X509
 		}
 
 		public DeclarationOfMajority(
-			DerGeneralizedTime dateOfBirth)
+            Asn1GeneralizedTime dateOfBirth)
 		{
 			this.declaration = new DerTaggedObject(false, 2, dateOfBirth);
 		}
@@ -155,14 +155,14 @@ namespace Org.BouncyCastle.Asn1.IsisMtt.X509
 			}
 		}
 
-		public virtual DerGeneralizedTime DateOfBirth
+		public virtual Asn1GeneralizedTime DateOfBirth
 		{
 			get
 			{
 				switch ((Choice) declaration.TagNo)
 				{
 					case Choice.DateOfBirth:
-						return DerGeneralizedTime.GetInstance(declaration, false);
+						return Asn1GeneralizedTime.GetInstance(declaration, false);
 					default:
 						return null;
 				}
diff --git a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
index 54e4280b9..693c0eaba 100644
--- a/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
+++ b/crypto/src/asn1/misc/MiscObjectIdentifiers.cs
@@ -40,15 +40,15 @@ namespace Org.BouncyCastle.Asn1.Misc
         // Novell
         //       iso/itu(2) country(16) us(840) organization(1) novell(113719)
         //
-        public static readonly string				Novell					= "2.16.840.1.113719";
-        public static readonly DerObjectIdentifier NovellSecurityAttribs	= new DerObjectIdentifier(Novell + ".1.9.4.1");
+        public static readonly DerObjectIdentifier Novell = new DerObjectIdentifier("2.16.840.1.113719");
+        public static readonly DerObjectIdentifier NovellSecurityAttribs = Novell.Branch("1.9.4.1");
 
         //
         // Entrust
         //       iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7)
         //
-        public static readonly string				Entrust					= "1.2.840.113533.7";
-        public static readonly DerObjectIdentifier EntrustVersionExtension = new DerObjectIdentifier(Entrust + ".65.0");
+        public static readonly DerObjectIdentifier Entrust = new DerObjectIdentifier("1.2.840.113533.7");
+        public static readonly DerObjectIdentifier EntrustVersionExtension = Entrust.Branch("65.0");
 
         public static readonly DerObjectIdentifier cast5CBC = new DerObjectIdentifier(Entrust+ ".66.10");
 
@@ -89,10 +89,32 @@ namespace Org.BouncyCastle.Asn1.Misc
         public static readonly DerObjectIdentifier id_blake2s224 = blake2.Branch("2.7");
         public static readonly DerObjectIdentifier id_blake2s256 = blake2.Branch("2.8");
 
+        public static readonly DerObjectIdentifier blake3 = blake2.Branch("3");
+
+        public static readonly DerObjectIdentifier blake3_256 = blake3.Branch("8");
+
         //
         // Scrypt
         public static readonly DerObjectIdentifier id_scrypt = new DerObjectIdentifier("1.3.6.1.4.1.11591.4.11");
 
-        public static readonly DerObjectIdentifier id_oracle_pkcs12_trusted_key_usage = new DerObjectIdentifier("2.16.840.1.113894.746875.1.1");
+        // Composite key/signature oid - prototyping
+        //
+        //    id-alg-composite OBJECT IDENTIFIER ::= {
+        //        iso(1)  identified-organization(3) dod(6) internet(1) private(4)
+        //        enterprise(1) OpenCA(18227) Algorithms(2) id-alg-composite(1) }
+        public static readonly DerObjectIdentifier id_alg_composite = new DerObjectIdentifier("1.3.6.1.4.1.18227.2.1");
+
+        // -- To be replaced by IANA
+        //
+        //id-composite-key OBJECT IDENTIFIER ::= {
+        //
+        //    joint-iso-itu-t(2) country(16) us(840) organization(1) entrust(114027)
+        //
+        //    Algorithm(80) Composite(4) CompositeKey(1)
+        public static readonly DerObjectIdentifier id_composite_key =
+            new DerObjectIdentifier("2.16.840.1.114027.80.4.1");
+
+        public static readonly DerObjectIdentifier id_oracle_pkcs12_trusted_key_usage =
+            new DerObjectIdentifier("2.16.840.1.113894.746875.1.1");
     }
 }
diff --git a/crypto/src/asn1/ocsp/CrlID.cs b/crypto/src/asn1/ocsp/CrlID.cs
index fc1e59d22..24dda4edf 100644
--- a/crypto/src/asn1/ocsp/CrlID.cs
+++ b/crypto/src/asn1/ocsp/CrlID.cs
@@ -7,7 +7,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
     {
         private readonly DerIA5String		crlUrl;
         private readonly DerInteger			crlNum;
-        private readonly DerGeneralizedTime	crlTime;
+        private readonly Asn1GeneralizedTime crlTime;
 
 		// TODO Add GetInstance method(s) and make this private?
 		public CrlID(Asn1Sequence seq)
@@ -23,7 +23,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
                     crlNum = DerInteger.GetInstance(o, true);
                     break;
                 case 2:
-                    crlTime = DerGeneralizedTime.GetInstance(o, true);
+                    crlTime = Asn1GeneralizedTime.GetInstance(o, true);
                     break;
                 default:
                     throw new ArgumentException("unknown tag number: " + o.TagNo);
@@ -41,7 +41,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			get { return crlNum; }
 		}
 
-		public DerGeneralizedTime CrlTime
+		public Asn1GeneralizedTime CrlTime
 		{
 			get { return crlTime; }
 		}
diff --git a/crypto/src/asn1/ocsp/ResponseData.cs b/crypto/src/asn1/ocsp/ResponseData.cs
index a5769c0fa..dfb234bc1 100644
--- a/crypto/src/asn1/ocsp/ResponseData.cs
+++ b/crypto/src/asn1/ocsp/ResponseData.cs
@@ -13,7 +13,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		private readonly bool                versionPresent;
 		private readonly DerInteger          version;
 		private readonly ResponderID         responderID;
-		private readonly DerGeneralizedTime  producedAt;
+		private readonly Asn1GeneralizedTime producedAt;
 		private readonly Asn1Sequence        responses;
 		private readonly X509Extensions      responseExtensions;
 
@@ -43,7 +43,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		public ResponseData(
 			DerInteger          version,
 			ResponderID         responderID,
-			DerGeneralizedTime  producedAt,
+            Asn1GeneralizedTime producedAt,
 			Asn1Sequence        responses,
 			X509Extensions      responseExtensions)
 		{
@@ -56,7 +56,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 
 		public ResponseData(
 			ResponderID         responderID,
-			DerGeneralizedTime  producedAt,
+            Asn1GeneralizedTime producedAt,
 			Asn1Sequence        responses,
 			X509Extensions      responseExtensions)
 			: this(V1, responderID, producedAt, responses, responseExtensions)
@@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			}
 
 			this.responderID = ResponderID.GetInstance(seq[index++]);
-			this.producedAt = (DerGeneralizedTime)seq[index++];
+			this.producedAt = (Asn1GeneralizedTime)seq[index++];
 			this.responses = (Asn1Sequence)seq[index++];
 
 			if (seq.Count > index)
@@ -110,7 +110,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			get { return responderID; }
 		}
 
-		public DerGeneralizedTime ProducedAt
+		public Asn1GeneralizedTime ProducedAt
 		{
 			get { return producedAt; }
 		}
diff --git a/crypto/src/asn1/ocsp/RevokedInfo.cs b/crypto/src/asn1/ocsp/RevokedInfo.cs
index c67be0678..e6438dd08 100644
--- a/crypto/src/asn1/ocsp/RevokedInfo.cs
+++ b/crypto/src/asn1/ocsp/RevokedInfo.cs
@@ -8,7 +8,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
     public class RevokedInfo
         : Asn1Encodable
     {
-        private readonly DerGeneralizedTime revocationTime;
+        private readonly Asn1GeneralizedTime revocationTime;
         private readonly CrlReason revocationReason;
 
 		public static RevokedInfo GetInstance(
@@ -35,13 +35,13 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		}
 
 		public RevokedInfo(
-			DerGeneralizedTime revocationTime)
+            Asn1GeneralizedTime revocationTime)
 			: this(revocationTime, null)
 		{
 		}
 
 		public RevokedInfo(
-            DerGeneralizedTime  revocationTime,
+            Asn1GeneralizedTime revocationTime,
             CrlReason           revocationReason)
         {
 			if (revocationTime == null)
@@ -54,7 +54,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 		private RevokedInfo(
             Asn1Sequence seq)
         {
-            this.revocationTime = (DerGeneralizedTime) seq[0];
+            this.revocationTime = (Asn1GeneralizedTime)seq[0];
 
 			if (seq.Count > 1)
             {
@@ -63,7 +63,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
             }
         }
 
-		public DerGeneralizedTime RevocationTime
+		public Asn1GeneralizedTime RevocationTime
 		{
 			get { return revocationTime; }
 		}
diff --git a/crypto/src/asn1/ocsp/SingleResponse.cs b/crypto/src/asn1/ocsp/SingleResponse.cs
index ecdf3dab0..42b451af7 100644
--- a/crypto/src/asn1/ocsp/SingleResponse.cs
+++ b/crypto/src/asn1/ocsp/SingleResponse.cs
@@ -10,15 +10,15 @@ namespace Org.BouncyCastle.Asn1.Ocsp
     {
         private readonly CertID              certID;
         private readonly CertStatus          certStatus;
-        private readonly DerGeneralizedTime  thisUpdate;
-        private readonly DerGeneralizedTime  nextUpdate;
+        private readonly Asn1GeneralizedTime thisUpdate;
+        private readonly Asn1GeneralizedTime nextUpdate;
         private readonly X509Extensions      singleExtensions;
 
 		public SingleResponse(
             CertID              certID,
             CertStatus          certStatus,
-            DerGeneralizedTime  thisUpdate,
-            DerGeneralizedTime  nextUpdate,
+            Asn1GeneralizedTime thisUpdate,
+            Asn1GeneralizedTime nextUpdate,
             X509Extensions      singleExtensions)
         {
             this.certID = certID;
@@ -33,11 +33,11 @@ namespace Org.BouncyCastle.Asn1.Ocsp
         {
             this.certID = CertID.GetInstance(seq[0]);
             this.certStatus = CertStatus.GetInstance(seq[1]);
-            this.thisUpdate = (DerGeneralizedTime)seq[2];
+            this.thisUpdate = (Asn1GeneralizedTime)seq[2];
 
 			if (seq.Count > 4)
             {
-                this.nextUpdate = DerGeneralizedTime.GetInstance(
+                this.nextUpdate = Asn1GeneralizedTime.GetInstance(
 					(Asn1TaggedObject) seq[3], true);
                 this.singleExtensions = X509Extensions.GetInstance(
 					(Asn1TaggedObject) seq[4], true);
@@ -48,7 +48,7 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 
 				if (o.TagNo == 0)
                 {
-                    this.nextUpdate = DerGeneralizedTime.GetInstance(o, true);
+                    this.nextUpdate = Asn1GeneralizedTime.GetInstance(o, true);
                 }
                 else
                 {
@@ -90,12 +90,12 @@ namespace Org.BouncyCastle.Asn1.Ocsp
 			get { return certStatus; }
 		}
 
-		public DerGeneralizedTime ThisUpdate
+		public Asn1GeneralizedTime ThisUpdate
 		{
 			get { return thisUpdate; }
 		}
 
-		public DerGeneralizedTime NextUpdate
+		public Asn1GeneralizedTime NextUpdate
 		{
 			get { return nextUpdate; }
 		}
diff --git a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs
index 1a6a5417a..570e0ded7 100644
--- a/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs
+++ b/crypto/src/asn1/pkcs/PKCSObjectIdentifiers.cs
@@ -138,11 +138,12 @@ namespace Org.BouncyCastle.Asn1.Pkcs
 
         public static readonly DerObjectIdentifier IdAlg = IdSmime.Branch("3");
 
-        public static readonly DerObjectIdentifier IdAlgEsdh        = IdAlg.Branch("5");
-        public static readonly DerObjectIdentifier IdAlgCms3DesWrap = IdAlg.Branch("6");
-        public static readonly DerObjectIdentifier IdAlgCmsRC2Wrap  = IdAlg.Branch("7");
-        public static readonly DerObjectIdentifier IdAlgPwriKek     = IdAlg.Branch("9");
-        public static readonly DerObjectIdentifier IdAlgSsdh        = IdAlg.Branch("10");
+        public static readonly DerObjectIdentifier IdAlgEsdh            = IdAlg.Branch("5");
+        public static readonly DerObjectIdentifier IdAlgCms3DesWrap     = IdAlg.Branch("6");
+        public static readonly DerObjectIdentifier IdAlgCmsRC2Wrap      = IdAlg.Branch("7");
+        public static readonly DerObjectIdentifier IdAlgZlibCompress    = IdAlg.Branch("8");
+        public static readonly DerObjectIdentifier IdAlgPwriKek         = IdAlg.Branch("9");
+        public static readonly DerObjectIdentifier IdAlgSsdh            = IdAlg.Branch("10");
 
         /*
          * <pre>
diff --git a/crypto/src/asn1/sec/ECPrivateKeyStructure.cs b/crypto/src/asn1/sec/ECPrivateKeyStructure.cs
index 769ab2970..89b87bf2b 100644
--- a/crypto/src/asn1/sec/ECPrivateKeyStructure.cs
+++ b/crypto/src/asn1/sec/ECPrivateKeyStructure.cs
@@ -1,6 +1,5 @@
 using System;
 
-using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Math;
 using Org.BouncyCastle.Utilities;
 
@@ -12,23 +11,20 @@ namespace Org.BouncyCastle.Asn1.Sec
     public class ECPrivateKeyStructure
         : Asn1Encodable
     {
-        private readonly Asn1Sequence seq;
+        private readonly Asn1Sequence m_seq;
 
         public static ECPrivateKeyStructure GetInstance(object obj)
         {
             if (obj == null)
                 return null;
-            if (obj is ECPrivateKeyStructure)
-                return (ECPrivateKeyStructure)obj;
+            if (obj is ECPrivateKeyStructure ecPrivateKeyStructure)
+                return ecPrivateKeyStructure;
             return new ECPrivateKeyStructure(Asn1Sequence.GetInstance(obj));
         }
 
         private ECPrivateKeyStructure(Asn1Sequence seq)
         {
-            if (seq == null)
-                throw new ArgumentNullException("seq");
-
-            this.seq = seq;
+            m_seq = seq ?? throw new ArgumentNullException(nameof(seq));
         }
 
         public ECPrivateKeyStructure(
@@ -53,9 +49,9 @@ namespace Org.BouncyCastle.Asn1.Sec
             Asn1Encodable   parameters)
         {
             if (key == null)
-                throw new ArgumentNullException("key");
+                throw new ArgumentNullException(nameof(key));
             if (orderBitLength < key.BitLength)
-                throw new ArgumentException("must be >= key bitlength", "orderBitLength");
+                throw new ArgumentException("must be >= key bitlength", nameof(orderBitLength));
 
             byte[] bytes = BigIntegers.AsUnsignedByteArray((orderBitLength + 7) / 8, key);
 
@@ -63,48 +59,42 @@ namespace Org.BouncyCastle.Asn1.Sec
                 new DerInteger(1),
                 new DerOctetString(bytes));
 
-            if (parameters != null)
-            {
-                v.Add(new DerTaggedObject(true, 0, parameters));
-            }
-
-            if (publicKey != null)
-            {
-                v.Add(new DerTaggedObject(true, 1, publicKey));
-            }
+            v.AddOptionalTagged(true, 0, parameters);
+            v.AddOptionalTagged(true, 1, publicKey);
 
-            this.seq = new DerSequence(v);
+            m_seq = new DerSequence(v);
         }
 
         public virtual BigInteger GetKey()
         {
-            Asn1OctetString octs = (Asn1OctetString) seq[1];
+            Asn1OctetString octs = (Asn1OctetString)m_seq[1];
 
             return new BigInteger(1, octs.GetOctets());
         }
 
         public virtual DerBitString GetPublicKey()
         {
-            return (DerBitString) GetObjectInTag(1);
+            return (DerBitString)GetObjectInTag(1, Asn1Tags.BitString);
         }
 
         public virtual Asn1Object GetParameters()
         {
-            return GetObjectInTag(0);
+            return GetObjectInTag(0, -1);
         }
 
-        private Asn1Object GetObjectInTag(int tagNo)
+        private Asn1Object GetObjectInTag(int tagNo, int baseTagNo)
         {
-            foreach (Asn1Encodable ae in seq)
+            foreach (Asn1Encodable ae in m_seq)
             {
                 Asn1Object obj = ae.ToAsn1Object();
 
-                if (obj is Asn1TaggedObject)
+                if (obj is Asn1TaggedObject tag)
                 {
-                    Asn1TaggedObject tag = (Asn1TaggedObject) obj;
-                    if (tag.TagNo == tagNo)
+                    if (tag.HasContextTag(tagNo))
                     {
-                        return tag.GetObject();
+                        return baseTagNo < 0
+                            ? tag.GetExplicitBaseObject().ToAsn1Object()
+                            : tag.GetBaseUniversal(true, baseTagNo);
                     }
                 }
             }
@@ -121,7 +111,7 @@ namespace Org.BouncyCastle.Asn1.Sec
          */
         public override Asn1Object ToAsn1Object()
         {
-            return seq;
+            return m_seq;
         }
     }
 }
diff --git a/crypto/src/asn1/tsp/TSTInfo.cs b/crypto/src/asn1/tsp/TSTInfo.cs
index 28a840e77..dde11494c 100644
--- a/crypto/src/asn1/tsp/TSTInfo.cs
+++ b/crypto/src/asn1/tsp/TSTInfo.cs
@@ -11,7 +11,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 		private readonly DerObjectIdentifier	tsaPolicyId;
 		private readonly MessageImprint			messageImprint;
 		private readonly DerInteger				serialNumber;
-		private readonly DerGeneralizedTime		genTime;
+		private readonly Asn1GeneralizedTime	genTime;
 		private readonly Accuracy				accuracy;
 		private readonly DerBoolean				ordering;
 		private readonly DerInteger				nonce;
@@ -49,7 +49,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 
 			// genTime
 			e.MoveNext();
-			genTime = DerGeneralizedTime.GetInstance(e.Current);
+			genTime = Asn1GeneralizedTime.GetInstance(e.Current);
 
 			// default for ordering
 			ordering = DerBoolean.False;
@@ -96,7 +96,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 			DerObjectIdentifier	tsaPolicyId,
 			MessageImprint		messageImprint,
 			DerInteger			serialNumber,
-			DerGeneralizedTime	genTime,
+            Asn1GeneralizedTime genTime,
 			Accuracy			accuracy,
 			DerBoolean			ordering,
 			DerInteger			nonce,
@@ -140,7 +140,7 @@ namespace Org.BouncyCastle.Asn1.Tsp
 			get { return accuracy; }
 		}
 
-		public DerGeneralizedTime GenTime
+		public Asn1GeneralizedTime GenTime
 		{
 			get { return genTime; }
 		}
diff --git a/crypto/src/asn1/util/Asn1Dump.cs b/crypto/src/asn1/util/Asn1Dump.cs
index 7bae766c3..6c005f512 100644
--- a/crypto/src/asn1/util/Asn1Dump.cs
+++ b/crypto/src/asn1/util/Asn1Dump.cs
@@ -206,12 +206,12 @@ namespace Org.BouncyCastle.Asn1.Utilities
                 buf.Append(indent);
                 buf.AppendLine("VideotexString(" + videotexString.GetString() + ")");
             }
-            else if (obj is DerUtcTime utcTime)
+            else if (obj is Asn1UtcTime utcTime)
             {
                 buf.Append(indent);
                 buf.AppendLine("UTCTime(" + utcTime.TimeString + ")");
             }
-            else if (obj is DerGeneralizedTime generalizedTime)
+            else if (obj is Asn1GeneralizedTime generalizedTime)
             {
                 buf.Append(indent);
                 buf.AppendLine("GeneralizedTime(" + generalizedTime.GetTime() + ")");
diff --git a/crypto/src/asn1/x509/AttCertValidityPeriod.cs b/crypto/src/asn1/x509/AttCertValidityPeriod.cs
index d31e07402..893bc0838 100644
--- a/crypto/src/asn1/x509/AttCertValidityPeriod.cs
+++ b/crypto/src/asn1/x509/AttCertValidityPeriod.cs
@@ -7,8 +7,8 @@ namespace Org.BouncyCastle.Asn1.X509
     public class AttCertValidityPeriod
         : Asn1Encodable
     {
-        private readonly DerGeneralizedTime	notBeforeTime;
-        private readonly DerGeneralizedTime	notAfterTime;
+        private readonly Asn1GeneralizedTime notBeforeTime;
+        private readonly Asn1GeneralizedTime notAfterTime;
 
 		public static AttCertValidityPeriod GetInstance(
             object obj)
@@ -39,24 +39,24 @@ namespace Org.BouncyCastle.Asn1.X509
 			if (seq.Count != 2)
 				throw new ArgumentException("Bad sequence size: " + seq.Count);
 
-			notBeforeTime = DerGeneralizedTime.GetInstance(seq[0]);
-			notAfterTime = DerGeneralizedTime.GetInstance(seq[1]);
+			notBeforeTime = Asn1GeneralizedTime.GetInstance(seq[0]);
+			notAfterTime = Asn1GeneralizedTime.GetInstance(seq[1]);
         }
 
 		public AttCertValidityPeriod(
-            DerGeneralizedTime	notBeforeTime,
-            DerGeneralizedTime	notAfterTime)
+            Asn1GeneralizedTime notBeforeTime,
+            Asn1GeneralizedTime notAfterTime)
         {
             this.notBeforeTime = notBeforeTime;
             this.notAfterTime = notAfterTime;
         }
 
-		public DerGeneralizedTime NotBeforeTime
+		public Asn1GeneralizedTime NotBeforeTime
 		{
 			get { return notBeforeTime; }
 		}
 
-		public DerGeneralizedTime NotAfterTime
+		public Asn1GeneralizedTime NotAfterTime
 		{
 			get { return notAfterTime; }
 		}
diff --git a/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs b/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
index 89e8de6cb..a87c2ee9e 100644
--- a/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
+++ b/crypto/src/asn1/x509/PrivateKeyUsagePeriod.cs
@@ -36,7 +36,7 @@ namespace Org.BouncyCastle.Asn1.X509
 			throw new ArgumentException("unknown object in GetInstance: " + Platform.GetTypeName(obj), "obj");
 		}
 
-		private DerGeneralizedTime _notBefore, _notAfter;
+		private Asn1GeneralizedTime _notBefore, _notAfter;
 
 		private PrivateKeyUsagePeriod(
 			Asn1Sequence seq)
@@ -45,21 +45,21 @@ namespace Org.BouncyCastle.Asn1.X509
 			{
 				if (tObj.TagNo == 0)
 				{
-					_notBefore = DerGeneralizedTime.GetInstance(tObj, false);
+					_notBefore = Asn1GeneralizedTime.GetInstance(tObj, false);
 				}
 				else if (tObj.TagNo == 1)
 				{
-					_notAfter = DerGeneralizedTime.GetInstance(tObj, false);
+					_notAfter = Asn1GeneralizedTime.GetInstance(tObj, false);
 				}
 			}
 		}
 
-		public DerGeneralizedTime NotBefore
+		public Asn1GeneralizedTime NotBefore
 		{
 			get { return _notBefore; }
 		}
 
-		public DerGeneralizedTime NotAfter
+		public Asn1GeneralizedTime NotAfter
 		{
 			get { return _notAfter; }
 		}
diff --git a/crypto/src/asn1/x509/TBSCertList.cs b/crypto/src/asn1/x509/TBSCertList.cs
index ab847d563..2b288850f 100644
--- a/crypto/src/asn1/x509/TBSCertList.cs
+++ b/crypto/src/asn1/x509/TBSCertList.cs
@@ -193,8 +193,8 @@ namespace Org.BouncyCastle.Asn1.X509
             thisUpdate = Time.GetInstance(seq[seqPos++]);
 
 			if (seqPos < seq.Count
-                && (seq[seqPos] is DerUtcTime
-                   || seq[seqPos] is DerGeneralizedTime
+                && (seq[seqPos] is Asn1UtcTime
+                   || seq[seqPos] is Asn1GeneralizedTime
                    || seq[seqPos] is Time))
             {
                 nextUpdate = Time.GetInstance(seq[seqPos++]);
diff --git a/crypto/src/asn1/x509/Time.cs b/crypto/src/asn1/x509/Time.cs
index 26c972904..8260043aa 100644
--- a/crypto/src/asn1/x509/Time.cs
+++ b/crypto/src/asn1/x509/Time.cs
@@ -22,7 +22,7 @@ namespace Org.BouncyCastle.Asn1.X509
         {
             if (time == null)
                 throw new ArgumentNullException("time");
-            if (!(time is DerUtcTime) && !(time is DerGeneralizedTime))
+            if (!(time is Asn1UtcTime) && !(time is Asn1GeneralizedTime))
                 throw new ArgumentException("unknown object passed to Time");
 
             this.time = time;
@@ -47,27 +47,26 @@ namespace Org.BouncyCastle.Asn1.X509
             }
         }
 
-        public static Time GetInstance(
-            object obj)
+        public static Time GetInstance(object obj)
         {
-            if (obj == null || obj is Time)
-                return (Time)obj;
-            if (obj is DerUtcTime)
-                return new Time((DerUtcTime)obj);
-            if (obj is DerGeneralizedTime)
-                return new Time((DerGeneralizedTime)obj);
+            if (obj == null)
+                return null;
+            if (obj is Time time)
+                return time;
+            if (obj is Asn1UtcTime utcTime)
+                return new Time(utcTime);
+            if (obj is Asn1GeneralizedTime generalizedTime)
+                return new Time(generalizedTime);
 
             throw new ArgumentException("unknown object in factory: " + Platform.GetTypeName(obj), "obj");
         }
 
         public string GetTime()
         {
-            if (time is DerUtcTime)
-            {
-                return ((DerUtcTime) time).AdjustedTimeString;
-            }
+            if (time is Asn1UtcTime utcTime)
+                return utcTime.AdjustedTimeString;
 
-            return ((DerGeneralizedTime) time).GetTime();
+            return ((Asn1GeneralizedTime)time).GetTime();
         }
 
         /// <summary>
@@ -78,14 +77,10 @@ namespace Org.BouncyCastle.Asn1.X509
         {
             try
             {
-                if (time is DerUtcTime)
-                {
-                    return ((DerUtcTime)time).ToAdjustedDateTime();
-                }
-                else
-                {
-                    return ((DerGeneralizedTime)time).ToDateTime();
-                }
+                if (time is Asn1UtcTime utcTime)
+                    return utcTime.ToAdjustedDateTime();
+
+                return ((Asn1GeneralizedTime)time).ToDateTime();
             }
             catch (FormatException e)
             {
diff --git a/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs b/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
index 20b525a48..9cbff1ef0 100644
--- a/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
+++ b/crypto/src/asn1/x509/V1TBSCertificateGenerator.cs
@@ -56,7 +56,7 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetStartDate(
-            DerUtcTime startDate)
+            Asn1UtcTime startDate)
         {
             this.startDate = new Time(startDate);
         }
@@ -68,7 +68,7 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetEndDate(
-            DerUtcTime endDate)
+            Asn1UtcTime endDate)
         {
             this.endDate = new Time(endDate);
         }
diff --git a/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs b/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
index 02580b5b8..c78c966b0 100644
--- a/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
+++ b/crypto/src/asn1/x509/V2AttributeCertificateInfoGenerator.cs
@@ -26,11 +26,13 @@ namespace Org.BouncyCastle.Asn1.X509
         internal AttCertIssuer			issuer;
         internal AlgorithmIdentifier	signature;
         internal DerInteger				serialNumber;
-//        internal AttCertValidityPeriod	attrCertValidityPeriod;
         internal Asn1EncodableVector	attributes;
         internal DerBitString			issuerUniqueID;
         internal X509Extensions			extensions;
-        internal DerGeneralizedTime		startDate, endDate;
+
+        // Note: validity period start/end dates stored directly
+        //internal AttCertValidityPeriod attrCertValidityPeriod;
+        internal Asn1GeneralizedTime    startDate, endDate;
 
 		public V2AttributeCertificateInfoGenerator()
         {
@@ -78,13 +80,13 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetStartDate(
-            DerGeneralizedTime startDate)
+            Asn1GeneralizedTime startDate)
         {
             this.startDate = startDate;
         }
 
 		public void SetEndDate(
-            DerGeneralizedTime endDate)
+            Asn1GeneralizedTime endDate)
         {
             this.endDate = endDate;
         }
diff --git a/crypto/src/asn1/x509/V2TBSCertListGenerator.cs b/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
index 1d58751fd..aa1a0b95d 100644
--- a/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
+++ b/crypto/src/asn1/x509/V2TBSCertListGenerator.cs
@@ -55,13 +55,13 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetThisUpdate(
-            DerUtcTime thisUpdate)
+            Asn1UtcTime thisUpdate)
         {
             this.thisUpdate = new Time(thisUpdate);
         }
 
 		public void SetNextUpdate(
-            DerUtcTime nextUpdate)
+            Asn1UtcTime nextUpdate)
         {
             this.nextUpdate = (nextUpdate != null)
 				?	new Time(nextUpdate)
@@ -90,7 +90,7 @@ namespace Org.BouncyCastle.Asn1.X509
 			crlEntries.Add(crlEntry);
 		}
 
-		public void AddCrlEntry(DerInteger userCertificate, DerUtcTime revocationDate, int reason)
+		public void AddCrlEntry(DerInteger userCertificate, Asn1UtcTime revocationDate, int reason)
 		{
 			AddCrlEntry(userCertificate, new Time(revocationDate), reason);
 		}
@@ -101,7 +101,7 @@ namespace Org.BouncyCastle.Asn1.X509
 		}
 
 		public void AddCrlEntry(DerInteger userCertificate, Time revocationDate, int reason,
-			DerGeneralizedTime invalidityDate)
+            Asn1GeneralizedTime invalidityDate)
 		{
             var extOids = new List<DerObjectIdentifier>();
             var extValues = new List<X509Extension>();
diff --git a/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs b/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
index beb469a0d..544582ddb 100644
--- a/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
+++ b/crypto/src/asn1/x509/V3TBSCertificateGenerator.cs
@@ -58,7 +58,7 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetStartDate(
-            DerUtcTime startDate)
+            Asn1UtcTime startDate)
         {
             this.startDate = new Time(startDate);
         }
@@ -70,7 +70,7 @@ namespace Org.BouncyCastle.Asn1.X509
         }
 
 		public void SetEndDate(
-            DerUtcTime endDate)
+            Asn1UtcTime endDate)
         {
             this.endDate = new Time(endDate);
         }
diff --git a/crypto/src/asn1/x509/X509DefaultEntryConverter.cs b/crypto/src/asn1/x509/X509DefaultEntryConverter.cs
index 7282ead26..d155efc5a 100644
--- a/crypto/src/asn1/x509/X509DefaultEntryConverter.cs
+++ b/crypto/src/asn1/x509/X509DefaultEntryConverter.cs
@@ -46,7 +46,7 @@ namespace Org.BouncyCastle.Asn1.X509
 
 			if (oid.Equals(X509Name.DateOfBirth)) // accept time string as well as # (for compatibility)
 			{
-				return new DerGeneralizedTime(value);
+				return new Asn1GeneralizedTime(value);
 			}
 
 			if (oid.Equals(X509Name.C)
diff --git a/crypto/src/asn1/x509/sigi/PersonalData.cs b/crypto/src/asn1/x509/sigi/PersonalData.cs
index 439039888..e8c75bf93 100644
--- a/crypto/src/asn1/x509/sigi/PersonalData.cs
+++ b/crypto/src/asn1/x509/sigi/PersonalData.cs
@@ -29,7 +29,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 	{
 		private readonly NameOrPseudonym	nameOrPseudonym;
 		private readonly BigInteger			nameDistinguisher;
-		private readonly DerGeneralizedTime	dateOfBirth;
+		private readonly Asn1GeneralizedTime dateOfBirth;
 		private readonly DirectoryString	placeOfBirth;
 		private readonly string				gender;
 		private readonly DirectoryString	postalAddress;
@@ -88,7 +88,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 					nameDistinguisher = DerInteger.GetInstance(o, false).Value;
 					break;
 				case 1:
-					dateOfBirth = DerGeneralizedTime.GetInstance(o, false);
+					dateOfBirth = Asn1GeneralizedTime.GetInstance(o, false);
 					break;
 				case 2:
 					placeOfBirth = DirectoryString.GetInstance(o, true);
@@ -118,7 +118,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 		public PersonalData(
 			NameOrPseudonym		nameOrPseudonym,
 			BigInteger			nameDistinguisher,
-			DerGeneralizedTime	dateOfBirth,
+            Asn1GeneralizedTime dateOfBirth,
 			DirectoryString		placeOfBirth,
 			string				gender,
 			DirectoryString		postalAddress)
@@ -141,7 +141,7 @@ namespace Org.BouncyCastle.Asn1.X509.SigI
 			get { return nameDistinguisher; }
 		}
 
-		public DerGeneralizedTime DateOfBirth
+		public Asn1GeneralizedTime DateOfBirth
 		{
 			get { return dateOfBirth; }
 		}