summary refs log tree commit diff
diff options
context:
space:
mode:
authorPeter Dettman <peter.dettman@bouncycastle.org>2023-07-27 18:49:31 +0700
committerPeter Dettman <peter.dettman@bouncycastle.org>2023-07-27 18:49:31 +0700
commita41e9dbc22fda83c4e81e5f3c56d49b1fa124e3f (patch)
treec63951f0ed67be114833bec65256e5e03174dbe6
parentRefactoring around stream calculators (diff)
downloadBouncyCastle.NET-ed25519-a41e9dbc22fda83c4e81e5f3c56d49b1fa124e3f.tar.xz
CMP, CRMF updates from bc-java
-rw-r--r--crypto/src/cmp/CertificateConfirmationContent.cs2
-rw-r--r--crypto/src/cmp/ProtectedPkiMessage.cs4
-rw-r--r--crypto/src/cmp/ProtectedPkiMessageBuilder.cs31
-rw-r--r--crypto/src/crmf/AuthenticatorControl.cs18
-rw-r--r--crypto/src/crmf/CertificateRepMessage.cs85
-rw-r--r--crypto/src/crmf/CertificateRepMessageBuilder.cs47
-rw-r--r--crypto/src/crmf/CertificateReqMessages.cs45
-rw-r--r--crypto/src/crmf/CertificateReqMessagesBuilder.cs28
-rw-r--r--crypto/src/crmf/CertificateRequestMessage.cs174
-rw-r--r--crypto/src/crmf/CertificateRequestMessageBuilder.cs175
-rw-r--r--crypto/src/crmf/CertificateResponse.cs94
-rw-r--r--crypto/src/crmf/CertificateResponseBuilder.cs104
-rw-r--r--crypto/src/crmf/EncryptedValueBuilder.cs10
-rw-r--r--crypto/src/crmf/EncryptedValueParser.cs97
-rw-r--r--crypto/src/crmf/PKMacBuilder.cs61
-rw-r--r--crypto/src/crmf/PKMacValueGenerator.cs28
-rw-r--r--crypto/src/crmf/PKMacValueVerifier.cs39
-rw-r--r--crypto/src/crmf/PkiArchiveControl.cs42
-rw-r--r--crypto/src/crmf/PkiArchiveControlBuilder.cs29
-rw-r--r--crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs48
-rw-r--r--crypto/src/crmf/RegTokenControl.cs22
21 files changed, 902 insertions, 281 deletions
diff --git a/crypto/src/cmp/CertificateConfirmationContent.cs b/crypto/src/cmp/CertificateConfirmationContent.cs
index 262a28531..edd4a28ed 100644
--- a/crypto/src/cmp/CertificateConfirmationContent.cs
+++ b/crypto/src/cmp/CertificateConfirmationContent.cs
@@ -14,7 +14,7 @@ namespace Org.BouncyCastle.Cmp
             IDigestAlgorithmFinder digestAlgorithmFinder)
         {
             if (!IsCertificateConfirmationContent(pkiBody.Type))
-                throw new ArgumentException("content of PkiBody wrong type: " + pkiBody.Type);
+                throw new ArgumentException("content of PKIBody wrong type: " + pkiBody.Type);
 
             var content = CertConfirmContent.GetInstance(pkiBody.Content);
 
diff --git a/crypto/src/cmp/ProtectedPkiMessage.cs b/crypto/src/cmp/ProtectedPkiMessage.cs
index a7fdd35a9..9e442c426 100644
--- a/crypto/src/cmp/ProtectedPkiMessage.cs
+++ b/crypto/src/cmp/ProtectedPkiMessage.cs
@@ -102,6 +102,9 @@ namespace Org.BouncyCastle.Cmp
         /// <exception cref="InvalidOperationException">if algorithm not MAC based, or an exception is thrown verifying the MAC.</exception>
         public virtual bool Verify(PKMacBuilder pkMacBuilder, char[] password)
         {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Verify(pkMacBuilder, password.AsSpan());
+#else
             var protectionAlgorithm = m_pkiMessage.Header.ProtectionAlg;
 
             if (!CmpObjectIdentifiers.passwordBasedMac.Equals(protectionAlgorithm.Algorithm))
@@ -113,6 +116,7 @@ namespace Org.BouncyCastle.Cmp
             var macFactory = pkMacBuilder.Build(password);
 
             return X509Utilities.VerifyMac(macFactory, CreateProtected(), m_pkiMessage.Protection);
+#endif
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
diff --git a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs
index ff4af5573..27d2cd0a1 100644
--- a/crypto/src/cmp/ProtectedPkiMessageBuilder.cs
+++ b/crypto/src/cmp/ProtectedPkiMessageBuilder.cs
@@ -4,6 +4,7 @@ using System.Collections.Generic;
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Cmp;
 using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crmf;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.X509;
 
@@ -86,25 +87,23 @@ namespace Org.BouncyCastle.Cmp
             return this;
         }
 
-        // TODO[crmf] Add CertificateReqMessages
-        //public ProtectedPkiMessageBuilder SetBody(int bodyType, CertificateReqMessages certificateReqMessages)
-        //{
-        //    if (!CertificateReqMessages.IsCertificateRequestMessages(bodyType))
-        //        throw new ArgumentException("body type " + bodyType + " does not match CMP type CertReqMessages");
+        public ProtectedPkiMessageBuilder SetBody(int bodyType, CertificateReqMessages certificateReqMessages)
+        {
+            if (!CertificateReqMessages.IsCertificateRequestMessages(bodyType))
+                throw new ArgumentException("body type " + bodyType + " does not match CMP type CertReqMessages");
 
-        //    m_body = new PkiBody(bodyType, certificateReqMessages.ToAsn1Structure());
-        //    return this;
-        //}
+            m_body = new PkiBody(bodyType, certificateReqMessages.ToAsn1Structure());
+            return this;
+        }
 
-        // TODO[crmf] Add CertificateRepMessage
-        //public ProtectedPkiMessageBuilder SetBody(int bodyType, CertificateRepMessage certificateRepMessage)
-        //{
-        //    if (!CertificateRepMessage.IsCertificateRepMessage(bodyType))
-        //        throw new ArgumentException("body type " + bodyType + " does not match CMP type CertRepMessage");
+        public ProtectedPkiMessageBuilder SetBody(int bodyType, CertificateRepMessage certificateRepMessage)
+        {
+            if (!CertificateRepMessage.IsCertificateRepMessage(bodyType))
+                throw new ArgumentException("body type " + bodyType + " does not match CMP type CertRepMessage");
 
-        //    m_body = new PkiBody(bodyType, certificateRepMessage.ToAsn1Structure());
-        //    return this;
-        //}
+            m_body = new PkiBody(bodyType, certificateRepMessage.ToAsn1Structure());
+            return this;
+        }
 
         public ProtectedPkiMessageBuilder SetBody(int bodyType,
             CertificateConfirmationContent certificateConfirmationContent)
diff --git a/crypto/src/crmf/AuthenticatorControl.cs b/crypto/src/crmf/AuthenticatorControl.cs
index fc546ede5..4a15db6cb 100644
--- a/crypto/src/crmf/AuthenticatorControl.cs
+++ b/crypto/src/crmf/AuthenticatorControl.cs
@@ -11,9 +11,7 @@ namespace Org.BouncyCastle.Crmf
     public class AuthenticatorControl
         : IControl
     {
-        private static readonly DerObjectIdentifier type = CrmfObjectIdentifiers.id_regCtrl_authenticator;
-
-        private readonly DerUtf8String token;
+        private readonly DerUtf8String m_token;
 
         /// <summary>
         /// Basic constructor - build from a UTF-8 string representing the token.
@@ -21,7 +19,7 @@ namespace Org.BouncyCastle.Crmf
         /// <param name="token">UTF-8 string representing the token.</param>
         public AuthenticatorControl(DerUtf8String token)
         {
-            this.token = token;
+            m_token = token;
         }
 
         /// <summary>
@@ -30,23 +28,17 @@ namespace Org.BouncyCastle.Crmf
         /// <param name="token">string representing the token.</param>
         public AuthenticatorControl(string token)
         {
-            this.token = new DerUtf8String(token);
+            m_token = new DerUtf8String(token);
         }
 
         /// <summary>
         /// Return the type of this control.
         /// </summary>
-        public DerObjectIdentifier Type
-        {
-            get { return type; }
-        }
+        public DerObjectIdentifier Type => CrmfObjectIdentifiers.id_regCtrl_authenticator;
 
         /// <summary>
         /// Return the token associated with this control (a UTF8String).
         /// </summary>
-        public Asn1Encodable Value
-        {
-            get { return token; }
-        }
+        public Asn1Encodable Value => m_token;
     }
 }
diff --git a/crypto/src/crmf/CertificateRepMessage.cs b/crypto/src/crmf/CertificateRepMessage.cs
new file mode 100644
index 000000000..756021981
--- /dev/null
+++ b/crypto/src/crmf/CertificateRepMessage.cs
@@ -0,0 +1,85 @@
+using System;
+using System.Collections.Generic;
+
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Crmf
+{
+    public class CertificateRepMessage
+    {
+        public static CertificateRepMessage FromPkiBody(PkiBody pkiBody)
+        {
+            if (!IsCertificateRepMessage(pkiBody.Type))
+                throw new ArgumentException("content of PKIBody wrong type: " + pkiBody.Type);
+
+            return new CertificateRepMessage(CertRepMessage.GetInstance(pkiBody.Content));
+        }
+
+        public static bool IsCertificateRepMessage(int bodyType)
+        {
+            switch (bodyType)
+            {
+            case PkiBody.TYPE_INIT_REP:
+            case PkiBody.TYPE_CERT_REP:
+            case PkiBody.TYPE_KEY_UPDATE_REP:
+            case PkiBody.TYPE_CROSS_CERT_REP:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        private readonly CertResponse[] m_resps;
+        private readonly CmpCertificate[] m_caCerts;
+
+        public CertificateRepMessage(CertRepMessage repMessage)
+        {
+            m_resps = repMessage.GetResponse();
+            m_caCerts = repMessage.GetCAPubs();
+        }
+
+        public virtual CertificateResponse[] GetResponses() => Array.ConvertAll(m_resps, resp => new CertificateResponse(resp));
+
+        public virtual X509Certificate[] GetX509Certificates()
+        {
+            List<X509Certificate> certs = new List<X509Certificate>();
+
+            foreach (var caCert in m_caCerts)
+            {
+                if (caCert.IsX509v3PKCert)
+                {
+                    certs.Add(new X509Certificate(caCert.X509v3PKCert));
+                }
+            }
+
+            return certs.ToArray();
+        }
+
+        /**
+         * Return true if the message only contains X.509 public key certificates.
+         *
+         * @return true if only X.509 PK, false otherwise.
+         */
+        public virtual bool IsOnlyX509PKCertificates()
+        {
+            bool isOnlyX509 = true;
+
+            foreach (var caCert in m_caCerts)
+            {
+                isOnlyX509 &= caCert.IsX509v3PKCert;
+            }
+
+            return isOnlyX509;
+        }
+
+        /**
+         * Return the actual CMP certificates - useful if the array also contains non-X509 PK certificates.
+         *
+         * @return CMPCertificate array
+         */
+        public virtual CmpCertificate[] GetCmpCertificates() => (CmpCertificate[])m_caCerts.Clone();
+
+        public virtual CertRepMessage ToAsn1Structure() => new CertRepMessage(m_caCerts, m_resps);
+    }
+}
diff --git a/crypto/src/crmf/CertificateRepMessageBuilder.cs b/crypto/src/crmf/CertificateRepMessageBuilder.cs
new file mode 100644
index 000000000..439d98b93
--- /dev/null
+++ b/crypto/src/crmf/CertificateRepMessageBuilder.cs
@@ -0,0 +1,47 @@
+using System;
+using System.Collections.Generic;
+
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Crmf
+{
+    /// <summary>Builder for a CertificateRepMessage.</summary>
+    public class CertificateRepMessageBuilder
+    {
+        private readonly List<CertResponse> m_responses = new List<CertResponse>();
+        private readonly CmpCertificate[] m_caCerts;
+
+        /**
+         * Base constructor which can accept 0 or more certificates representing the CA plus its chain.
+         *
+         * @param caCerts the CA public key and it's support certificates (optional)
+         */
+        public CertificateRepMessageBuilder(params X509Certificate[] caCerts)
+        {
+            m_caCerts = Array.ConvertAll(caCerts, caCert => new CmpCertificate(caCert.CertificateStructure));
+        }
+
+        public virtual CertificateRepMessageBuilder AddCertificateResponse(CertificateResponse response)
+        {
+            m_responses.Add(response.ToAsn1Structure());
+            return this;
+        }
+
+        public virtual CertificateRepMessage Build()
+        {
+            var caPubs = m_caCerts;
+            if (caPubs.Length < 1)
+            {
+                // older versions of CertRepMessage need null if no caCerts.
+                caPubs = null;
+            }
+
+            CertRepMessage repMessage = new CertRepMessage(caPubs, m_responses.ToArray());
+
+            m_responses.Clear();
+
+            return new CertificateRepMessage(repMessage);
+        }
+    }
+}
diff --git a/crypto/src/crmf/CertificateReqMessages.cs b/crypto/src/crmf/CertificateReqMessages.cs
new file mode 100644
index 000000000..db465a352
--- /dev/null
+++ b/crypto/src/crmf/CertificateReqMessages.cs
@@ -0,0 +1,45 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Crmf;
+
+namespace Org.BouncyCastle.Crmf
+{
+    public class CertificateReqMessages
+    {
+        public static CertificateReqMessages FromPkiBody(PkiBody pkiBody)
+        {
+            if (!IsCertificateRequestMessages(pkiBody.Type))
+                throw new ArgumentException("content of PKIBody wrong type: " + pkiBody.Type);
+
+            return new CertificateReqMessages(CertReqMessages.GetInstance(pkiBody.Content));
+        }
+
+        public static bool IsCertificateRequestMessages(int bodyType)
+        {
+            switch (bodyType)
+            {
+            case PkiBody.TYPE_INIT_REQ:
+            case PkiBody.TYPE_CERT_REQ:
+            case PkiBody.TYPE_KEY_UPDATE_REQ:
+            case PkiBody.TYPE_KEY_RECOVERY_REQ:
+            case PkiBody.TYPE_CROSS_CERT_REQ:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        private readonly CertReqMsg[] m_reqs;
+
+        public CertificateReqMessages(CertReqMessages certReqMessages)
+        {
+            m_reqs = certReqMessages.ToCertReqMsgArray();
+        }
+
+        public virtual CertificateRequestMessage[] GetRequests() =>
+            Array.ConvertAll(m_reqs, req => new CertificateRequestMessage(req));
+
+        public virtual CertReqMessages ToAsn1Structure() => new CertReqMessages(m_reqs);
+    }
+}
diff --git a/crypto/src/crmf/CertificateReqMessagesBuilder.cs b/crypto/src/crmf/CertificateReqMessagesBuilder.cs
new file mode 100644
index 000000000..813f1d9e3
--- /dev/null
+++ b/crypto/src/crmf/CertificateReqMessagesBuilder.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+
+using Org.BouncyCastle.Asn1.Crmf;
+
+namespace Org.BouncyCastle.Crmf
+{
+    public class CertificateReqMessagesBuilder
+    {
+        private readonly List<CertReqMsg> m_requests = new List<CertReqMsg>();
+
+        public CertificateReqMessagesBuilder()
+        {
+        }
+
+        public virtual void AddRequest(CertificateRequestMessage request) => m_requests.Add(request.ToAsn1Structure());
+
+        public virtual CertificateReqMessages Build()
+        {
+            CertificateReqMessages certificateReqMessages = new CertificateReqMessages(
+                new CertReqMessages(m_requests.ToArray()));
+
+            m_requests.Clear();
+
+            return certificateReqMessages;
+        }
+    }
+}
diff --git a/crypto/src/crmf/CertificateRequestMessage.cs b/crypto/src/crmf/CertificateRequestMessage.cs
index d71e85e1f..8b8feb3f4 100644
--- a/crypto/src/crmf/CertificateRequestMessage.cs
+++ b/crypto/src/crmf/CertificateRequestMessage.cs
@@ -9,70 +9,61 @@ namespace Org.BouncyCastle.Crmf
 {
     public class CertificateRequestMessage
     {
-        public static readonly int popRaVerified = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_RA_VERIFIED;
-        public static readonly int popSigningKey = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_SIGNING_KEY;
-        public static readonly int popKeyEncipherment = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
-        public static readonly int popKeyAgreement = Org.BouncyCastle.Asn1.Crmf.ProofOfPossession.TYPE_KEY_AGREEMENT;
+        public static readonly int popRaVerified = Asn1.Crmf.ProofOfPossession.TYPE_RA_VERIFIED;
+        public static readonly int popSigningKey = Asn1.Crmf.ProofOfPossession.TYPE_SIGNING_KEY;
+        public static readonly int popKeyEncipherment = Asn1.Crmf.ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
+        public static readonly int popKeyAgreement = Asn1.Crmf.ProofOfPossession.TYPE_KEY_AGREEMENT;
 
-        private readonly CertReqMsg certReqMsg;
-        private readonly Controls controls;
+        private readonly CertReqMsg m_certReqMsg;
+        private readonly Controls m_controls;
 
-        private static CertReqMsg ParseBytes(byte[] encoding)
-        {
-            return CertReqMsg.GetInstance(encoding);
-        }
+        private static CertReqMsg ParseBytes(byte[] encoding) => CertReqMsg.GetInstance(encoding);
 
         /// <summary>
         /// Create a CertificateRequestMessage from the passed in bytes.
         /// </summary>
         /// <param name="encoded">BER/DER encoding of the CertReqMsg structure.</param>
         public CertificateRequestMessage(byte[] encoded)
-            : this(CertReqMsg.GetInstance(encoded))
+            : this(ParseBytes(encoded))
         {
         }
 
         public CertificateRequestMessage(CertReqMsg certReqMsg)
         {
-            this.certReqMsg = certReqMsg;
-            this.controls = certReqMsg.CertReq.Controls;
+            m_certReqMsg = certReqMsg;
+            m_controls = certReqMsg.CertReq.Controls;
         }
 
         /// <summary>
         /// Return the underlying ASN.1 object defining this CertificateRequestMessage object.
         /// </summary>
         /// <returns>A CertReqMsg</returns>
-        public CertReqMsg ToAsn1Structure()
-        {
-            return certReqMsg;
-        }
+        public CertReqMsg ToAsn1Structure() => m_certReqMsg;
+
+        /// <summary>
+        /// Return the certificate request ID for this message.
+        /// </summary>
+        /// <returns>the certificate request ID.</returns>
+        public DerInteger GetCertReqID() => m_certReqMsg.CertReq.CertReqID;
 
         /// <summary>
         /// Return the certificate template contained in this message.
         /// </summary>
         /// <returns>a CertTemplate structure.</returns>
-        public CertTemplate GetCertTemplate()
-        {
-            return this.certReqMsg.CertReq.CertTemplate;
-        }
+        public CertTemplate GetCertTemplate() => m_certReqMsg.CertReq.CertTemplate;
 
         /// <summary>
         /// Return whether or not this request has control values associated with it.
         /// </summary>
         /// <returns>true if there are control values present, false otherwise.</returns>
-        public bool HasControls
-        {
-            get { return controls != null; }
-        }
+        public bool HasControls => m_controls != null;
 
         /// <summary>
         /// Return whether or not this request has a specific type of control value.
         /// </summary>
         /// <param name="objectIdentifier">the type OID for the control value we are checking for.</param>
         /// <returns>true if a control value of type is present, false otherwise.</returns>
-        public bool HasControl(DerObjectIdentifier objectIdentifier)
-        {
-            return FindControl(objectIdentifier) != null;
-        }
+        public bool HasControl(DerObjectIdentifier objectIdentifier) => FindControl(objectIdentifier) != null;
 
         /// <summary>
         /// Return a control value of the specified type.
@@ -84,32 +75,26 @@ namespace Org.BouncyCastle.Crmf
             AttributeTypeAndValue found = FindControl(type);
             if (found != null)
             {
-                if (found.Type.Equals(CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions))
-                {
+                var oid = found.Type;
+
+                if (CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions.Equals(oid))
                     return new PkiArchiveControl(PkiArchiveOptions.GetInstance(found.Value));
-                }
 
-                if (found.Type.Equals(CrmfObjectIdentifiers.id_regCtrl_regToken))
-                {
+                if (CrmfObjectIdentifiers.id_regCtrl_regToken.Equals(oid))
                     return new RegTokenControl(DerUtf8String.GetInstance(found.Value));
-                }
 
-                if (found.Type.Equals(CrmfObjectIdentifiers.id_regCtrl_authenticator))
-                {
+                if (CrmfObjectIdentifiers.id_regCtrl_authenticator.Equals(oid))
                     return new AuthenticatorControl(DerUtf8String.GetInstance(found.Value));
-                }
             }
             return null;
         }
 
         public AttributeTypeAndValue FindControl(DerObjectIdentifier type)
         {
-            if (controls == null)
-            {
+            if (m_controls == null)
                 return null;
-            }
 
-            AttributeTypeAndValue[] tAndV = controls.ToAttributeTypeAndValueArray();
+            AttributeTypeAndValue[] tAndV = m_controls.ToAttributeTypeAndValueArray();
             AttributeTypeAndValue found = null;
 
             for (int i = 0; i < tAndV.Length; i++)
@@ -128,19 +113,13 @@ namespace Org.BouncyCastle.Crmf
         /// Return whether or not this request message has a proof-of-possession field in it.
         /// </summary>
         /// <returns>true if proof-of-possession is present, false otherwise.</returns>
-        public bool HasProofOfPossession
-        {
-            get { return certReqMsg.Pop != null; }
-        }
+        public bool HasProofOfPossession => m_certReqMsg.Pop != null;
 
         /// <summary>
         /// Return the type of the proof-of-possession this request message provides.
         /// </summary>
         /// <returns>one of: popRaVerified, popSigningKey, popKeyEncipherment, popKeyAgreement</returns>
-        public int ProofOfPossession
-        {
-            get { return certReqMsg.Pop.Type; }
-        }
+        public int ProofOfPossession => m_certReqMsg.Pop.Type;
 
         /// <summary>
         /// Return whether or not the proof-of-possession (POP) is of the type popSigningKey and
@@ -151,16 +130,14 @@ namespace Org.BouncyCastle.Crmf
         {
             get
             {
-                ProofOfPossession pop = certReqMsg.Pop;
+                ProofOfPossession pop = m_certReqMsg.Pop;
 
-                if (pop.Type == popSigningKey)
-                {
-                    PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object);
+                if (pop.Type != popSigningKey)
+                    return false;
 
-                    return popoSign.PoposkInput.PublicKeyMac != null;
-                }
+                PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object);
 
-                return false;
+                return popoSign.PoposkInput.PublicKeyMac != null;
             }
         }
 
@@ -173,19 +150,77 @@ namespace Org.BouncyCastle.Crmf
         /// <exception cref="InvalidOperationException">if POP not appropriate.</exception>
         public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider)
         {
-            ProofOfPossession pop = certReqMsg.Pop;
-            if (pop.Type == popSigningKey)
-            {
-                PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object);
-                if (popoSign.PoposkInput != null && popoSign.PoposkInput.PublicKeyMac != null)
-                    throw new InvalidOperationException("verification requires password check");
+            ProofOfPossession pop = m_certReqMsg.Pop;
+            if (pop.Type != popSigningKey)
+                throw new InvalidOperationException("not Signing Key type of proof of possession");
 
-                return VerifySignature(verifierProvider, popoSign);
-            }
+            PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object);
+            if (popoSign.PoposkInput != null && popoSign.PoposkInput.PublicKeyMac != null)
+                throw new InvalidOperationException("verification requires password check");
 
-            throw new InvalidOperationException("not Signing Key type of proof of possession");
+            return VerifySignature(verifierProvider, popoSign);
+        }
+
+        /// <summary>
+        /// Return whether or not a signing key proof-of-possession (POP), with an associated PKMAC, is valid.
+        /// </summary>
+        /// <param name="verifierProvider">a provider that can produce content verifiers for the signature contained in this POP.</param>
+        /// <param name="macBuilder">a suitable PKMacBuilder to create the MAC verifier.</param>
+        /// <param name="password">the password used to key the MAC calculation.</param>
+        /// <returns>true if the POP is valid, false otherwise.</returns>
+        /// <exception cref="InvalidOperationException">if there is a problem in verification or content verifier creation.</exception>
+        /// <exception cref="InvalidOperationException">if POP not appropriate.</exception>
+        public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider, PKMacBuilder macBuilder,
+            char[] password)
+        {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return IsValidSigningKeyPop(verifierProvider, macBuilder, password.AsSpan());
+#else
+            ProofOfPossession pop = m_certReqMsg.Pop;
+            if (pop.Type != popSigningKey)
+                throw new InvalidOperationException("not Signing Key type of proof of possession");
+
+            PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object);
+            if (popoSign.PoposkInput == null || popoSign.PoposkInput.Sender != null)
+                throw new InvalidOperationException("no PKMAC present in proof of possession");
+
+            PKMacValue mac = popoSign.PoposkInput.PublicKeyMac;
+            PKMacValueVerifier macVerifier = new PKMacValueVerifier(macBuilder);
+
+            return macVerifier.IsValid(mac, password, GetCertTemplate().PublicKey)
+                && VerifySignature(verifierProvider, popoSign);
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        /// <summary>
+        /// Return whether or not a signing key proof-of-possession (POP), with an associated PKMAC, is valid.
+        /// </summary>
+        /// <param name="verifierProvider">a provider that can produce content verifiers for the signature contained in this POP.</param>
+        /// <param name="macBuilder">a suitable PKMacBuilder to create the MAC verifier.</param>
+        /// <param name="password">the password used to key the MAC calculation.</param>
+        /// <returns>true if the POP is valid, false otherwise.</returns>
+        /// <exception cref="InvalidOperationException">if there is a problem in verification or content verifier creation.</exception>
+        /// <exception cref="InvalidOperationException">if POP not appropriate.</exception>
+        public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider, PKMacBuilder macBuilder,
+            ReadOnlySpan<char> password)
+        {
+            ProofOfPossession pop = m_certReqMsg.Pop;
+            if (pop.Type != popSigningKey)
+                throw new InvalidOperationException("not Signing Key type of proof of possession");
+
+            PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object);
+            if (popoSign.PoposkInput == null || popoSign.PoposkInput.Sender != null)
+                throw new InvalidOperationException("no PKMAC present in proof of possession");
+
+            PKMacValue mac = popoSign.PoposkInput.PublicKeyMac;
+            PKMacValueVerifier macVerifier = new PKMacValueVerifier(macBuilder);
+
+            return macVerifier.IsValid(mac, password, GetCertTemplate().PublicKey)
+                && VerifySignature(verifierProvider, popoSign);
+        }
+#endif
+
         private bool VerifySignature(IVerifierFactoryProvider verifierFactoryProvider, PopoSigningKey signKey)
         {
             var verifierFactory = verifierFactoryProvider.CreateVerifierFactory(signKey.AlgorithmIdentifier);
@@ -193,7 +228,7 @@ namespace Org.BouncyCastle.Crmf
             Asn1Encodable asn1Encodable = signKey.PoposkInput;
             if (asn1Encodable == null)
             {
-                asn1Encodable = certReqMsg.CertReq;
+                asn1Encodable = m_certReqMsg.CertReq;
             }
 
             return X509.X509Utilities.VerifySignature(verifierFactory, asn1Encodable, signKey.Signature);
@@ -203,9 +238,6 @@ namespace Org.BouncyCastle.Crmf
         /// Return the ASN.1 encoding of the certReqMsg we wrap.
         /// </summary>
         /// <returns>a byte array containing the binary encoding of the certReqMsg.</returns>
-        public byte[] GetEncoded()
-        {
-            return certReqMsg.GetEncoded();
-        }
+        public byte[] GetEncoded() => m_certReqMsg.GetEncoded();
     }
 }
diff --git a/crypto/src/crmf/CertificateRequestMessageBuilder.cs b/crypto/src/crmf/CertificateRequestMessageBuilder.cs
index dc5bc6224..84c9b7966 100644
--- a/crypto/src/crmf/CertificateRequestMessageBuilder.cs
+++ b/crypto/src/crmf/CertificateRequestMessageBuilder.cs
@@ -11,31 +11,38 @@ namespace Org.BouncyCastle.Crmf
 {
     public class CertificateRequestMessageBuilder
     {
-        private readonly BigInteger _certReqId;
-        private X509ExtensionsGenerator _extGenerator;
-        private CertTemplateBuilder _templateBuilder;
-        private IList<IControl> m_controls = new List<IControl>();
-        private ISignatureFactory _popSigner;
-        private PKMacBuilder _pkMacBuilder;
-        private char[] _password;
-        private GeneralName _sender;
-        private int _popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
-        private PopoPrivKey _popoPrivKey;
-        private Asn1Null _popRaVerified;
-        private PKMacValue _agreeMac;
+        private readonly List<IControl> m_controls = new List<IControl>();
+        private readonly X509ExtensionsGenerator m_extGenerator = new X509ExtensionsGenerator();
+        private readonly CertTemplateBuilder m_templateBuilder = new CertTemplateBuilder();
+
+        private readonly BigInteger m_certReqID;
+
+        private ISignatureFactory m_popSigner = null;
+        private PKMacBuilder m_pkMacBuilder = null;
+        private char[] m_password = null;
+        private GeneralName m_sender = null;
+        private int m_popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
+        private PopoPrivKey m_popoPrivKey = null;
+        private Asn1Null m_popRaVerified = null;
+        private PKMacValue m_agreeMac = null;
+        private AttributeTypeAndValue[] m_regInfo = null;
 
         public CertificateRequestMessageBuilder(BigInteger certReqId)
         {
-            this._certReqId = certReqId;
-            this._extGenerator = new X509ExtensionsGenerator();
-            this._templateBuilder = new CertTemplateBuilder();
+            m_certReqID = certReqId;
+        }
+
+        public CertificateRequestMessageBuilder SetRegInfo(AttributeTypeAndValue[] regInfo)
+        {
+            m_regInfo = regInfo;
+            return this;
         }
 
         public CertificateRequestMessageBuilder SetPublicKey(SubjectPublicKeyInfo publicKeyInfo)
         {
             if (publicKeyInfo != null)
             {
-                _templateBuilder.SetPublicKey(publicKeyInfo);
+                m_templateBuilder.SetPublicKey(publicKeyInfo);
             }
 
             return this;
@@ -45,7 +52,7 @@ namespace Org.BouncyCastle.Crmf
         {
             if (issuer != null)
             {
-                _templateBuilder.SetIssuer(issuer);
+                m_templateBuilder.SetIssuer(issuer);
             }
 
             return this;
@@ -55,7 +62,7 @@ namespace Org.BouncyCastle.Crmf
         {
             if (subject != null)
             {
-                _templateBuilder.SetSubject(subject);
+                m_templateBuilder.SetSubject(subject);
             }
 
             return this;
@@ -65,7 +72,17 @@ namespace Org.BouncyCastle.Crmf
         {
             if (serialNumber != null)
             {
-                _templateBuilder.SetSerialNumber(new DerInteger(serialNumber));
+                m_templateBuilder.SetSerialNumber(new DerInteger(serialNumber));
+            }
+
+            return this;
+        }
+
+        public CertificateRequestMessageBuilder SetSerialNumber(DerInteger serialNumber)
+        {
+            if (serialNumber != null)
+            {
+                m_templateBuilder.SetSerialNumber(serialNumber);
             }
 
             return this;
@@ -73,21 +90,21 @@ namespace Org.BouncyCastle.Crmf
 
         public CertificateRequestMessageBuilder SetValidity(DateTime? notBefore, DateTime? notAfter)
         {
-            _templateBuilder.SetValidity(new OptionalValidity(CreateTime(notBefore), CreateTime(notAfter)));
+            m_templateBuilder.SetValidity(new OptionalValidity(CreateTime(notBefore), CreateTime(notAfter)));
             return this;
         }
 
         public CertificateRequestMessageBuilder AddExtension(DerObjectIdentifier oid, bool critical,
             Asn1Encodable value)
         {
-            _extGenerator.AddExtension(oid, critical, value);
+            m_extGenerator.AddExtension(oid, critical, value);
             return this;
         }
 
         public CertificateRequestMessageBuilder AddExtension(DerObjectIdentifier oid, bool critical,
             byte[] value)
         {
-            _extGenerator.AddExtension(oid, critical, value);
+            m_extGenerator.AddExtension(oid, critical, value);
             return this;
         }
 
@@ -97,69 +114,53 @@ namespace Org.BouncyCastle.Crmf
             return this;
         }
 
-        public CertificateRequestMessageBuilder SetProofOfPossessionSignKeySigner(ISignatureFactory popoSignatureFactory)
+        public CertificateRequestMessageBuilder SetProofOfPossessionSignKeySigner(
+            ISignatureFactory popoSignatureFactory)
         {
-            if (_popoPrivKey != null || _popRaVerified != null || _agreeMac != null)
-            {
+            if (m_popoPrivKey != null || m_popRaVerified != null || m_agreeMac != null)
                 throw new InvalidOperationException("only one proof of possession is allowed.");
-            }
-
-            this._popSigner = popoSignatureFactory;
 
+            m_popSigner = popoSignatureFactory;
             return this;
         }
 
         public CertificateRequestMessageBuilder SetProofOfPossessionSubsequentMessage(SubsequentMessage msg)
         {
-            if (_popoPrivKey != null || _popRaVerified != null || _agreeMac != null)
-            {
+            if (m_popoPrivKey != null || m_popRaVerified != null || m_agreeMac != null)
                 throw new InvalidOperationException("only one proof of possession is allowed.");
-            }
-
-            this._popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
-            this._popoPrivKey = new PopoPrivKey(msg);
 
+            m_popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
+            m_popoPrivKey = new PopoPrivKey(msg);
             return this;
         }
 
-
         public CertificateRequestMessageBuilder SetProofOfPossessionSubsequentMessage(int type, SubsequentMessage msg)
         {
-            if (_popoPrivKey != null || _popRaVerified != null || _agreeMac != null)
-            {
+            if (m_popoPrivKey != null || m_popRaVerified != null || m_agreeMac != null)
                 throw new InvalidOperationException("only one proof of possession is allowed.");
-            }
-
             if (type != ProofOfPossession.TYPE_KEY_ENCIPHERMENT && type != ProofOfPossession.TYPE_KEY_AGREEMENT)
-            {
-                throw new ArgumentException("type must be ProofOfPossession.TYPE_KEY_ENCIPHERMENT || ProofOfPossession.TYPE_KEY_AGREEMENT");
-            }
+                throw new ArgumentException("type must be ProofOfPossession.TYPE_KEY_ENCIPHERMENT or ProofOfPossession.TYPE_KEY_AGREEMENT");
 
-            this._popoType = type;
-            this._popoPrivKey = new PopoPrivKey(msg);
+            m_popoType = type;
+            m_popoPrivKey = new PopoPrivKey(msg);
             return this;
         }
 
         public CertificateRequestMessageBuilder SetProofOfPossessionAgreeMac(PKMacValue macValue)
         {
-            if (_popSigner != null || _popRaVerified != null || _popoPrivKey != null)
-            {
+            if (m_popSigner != null || m_popRaVerified != null || m_popoPrivKey != null)
                 throw new InvalidOperationException("only one proof of possession allowed");
-            }
 
-            this._agreeMac = macValue;
+            m_agreeMac = macValue;
             return this;
         }
 
         public CertificateRequestMessageBuilder SetProofOfPossessionRaVerified()
         {
-            if (_popSigner != null || _popoPrivKey != null)
-            {
+            if (m_popSigner != null || m_popoPrivKey != null)
                 throw new InvalidOperationException("only one proof of possession allowed");
-            }
-
-            this._popRaVerified = DerNull.Instance;
 
+            m_popRaVerified = DerNull.Instance;
             return this;
         }
 
@@ -171,11 +172,20 @@ namespace Org.BouncyCastle.Crmf
 
         public CertificateRequestMessageBuilder SetAuthInfoPKMacBuilder(PKMacBuilder pkmacFactory, char[] password)
         {
-            this._pkMacBuilder = pkmacFactory;
-            this._password = password;
+            m_pkMacBuilder = pkmacFactory;
+            m_password = password;
+            return this;
+        }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public CertificateRequestMessageBuilder SetAuthInfoPKMacBuilder(PKMacBuilder pkmacFactory,
+            ReadOnlySpan<char> password)
+        {
+            m_pkMacBuilder = pkmacFactory;
+            m_password = password.ToArray();
             return this;
         }
+#endif
 
         public CertificateRequestMessageBuilder SetAuthInfoSender(X509Name sender)
         {
@@ -184,20 +194,21 @@ namespace Org.BouncyCastle.Crmf
 
         public CertificateRequestMessageBuilder SetAuthInfoSender(GeneralName sender)
         {
-            this._sender = sender;
+            m_sender = sender;
             return this;
         }
 
         public CertificateRequestMessage Build()
         {
-            Asn1EncodableVector v = new Asn1EncodableVector(new DerInteger(this._certReqId));
+            Asn1EncodableVector v = new Asn1EncodableVector(3);
+            v.Add(new DerInteger(m_certReqID));
 
-            if (!this._extGenerator.IsEmpty)
+            if (!m_extGenerator.IsEmpty)
             {
-                this._templateBuilder.SetExtensions(_extGenerator.Generate());
+                m_templateBuilder.SetExtensions(m_extGenerator.Generate());
             }
 
-            v.Add(_templateBuilder.Build());
+            v.Add(m_templateBuilder.Build());
 
             if (m_controls.Count > 0)
             {
@@ -213,52 +224,54 @@ namespace Org.BouncyCastle.Crmf
 
             CertRequest request = CertRequest.GetInstance(new DerSequence(v));
 
-            v = new Asn1EncodableVector(request);
-
-            if (_popSigner != null)
+            ProofOfPossession proofOfPossession;
+            if (m_popSigner != null)
             {
                 CertTemplate template = request.CertTemplate;
 
+                ProofOfPossessionSigningKeyBuilder builder;
                 if (template.Subject == null || template.PublicKey == null)
                 {
                     SubjectPublicKeyInfo pubKeyInfo = request.CertTemplate.PublicKey;
 
-                    ProofOfPossessionSigningKeyBuilder builder = new ProofOfPossessionSigningKeyBuilder(pubKeyInfo);
+                    builder = new ProofOfPossessionSigningKeyBuilder(pubKeyInfo);
 
-                    if (_sender != null)
+                    if (m_sender != null)
                     {
-                        builder.SetSender(_sender);
+                        builder.SetSender(m_sender);
                     }
                     else
                     {
-                        //PKMACValueGenerator pkmacGenerator = new PKMACValueGenerator(_pkmacBuilder);
-
-                        builder.SetPublicKeyMac(_pkMacBuilder, _password);
+                        builder.SetPublicKeyMac(m_pkMacBuilder, m_password);
                     }
-
-                    v.Add(new ProofOfPossession(builder.Build(_popSigner)));
                 }
                 else
                 {
-                    ProofOfPossessionSigningKeyBuilder builder = new ProofOfPossessionSigningKeyBuilder(request);
-
-                    v.Add(new ProofOfPossession(builder.Build(_popSigner)));
+                    builder = new ProofOfPossessionSigningKeyBuilder(request);
                 }
+
+                proofOfPossession = new ProofOfPossession(builder.Build(m_popSigner));
             }
-            else if (_popoPrivKey != null)
+            else if (m_popoPrivKey != null)
             {
-                v.Add(new ProofOfPossession(_popoType, _popoPrivKey));
+                proofOfPossession = new ProofOfPossession(m_popoType, m_popoPrivKey);
             }
-            else if (_agreeMac != null)
+            else if (m_agreeMac != null)
             {
-                v.Add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_AGREEMENT, new PopoPrivKey(_agreeMac)));
+                proofOfPossession = new ProofOfPossession(ProofOfPossession.TYPE_KEY_AGREEMENT, new PopoPrivKey(m_agreeMac));
             }
-            else if (_popRaVerified != null)
+            else if (m_popRaVerified != null)
             {
-                v.Add(new ProofOfPossession());
+                proofOfPossession = new ProofOfPossession();
             }
+            else
+            {
+                proofOfPossession = new ProofOfPossession();
+            }
+
+            CertReqMsg certReqMsg = new CertReqMsg(request, proofOfPossession, m_regInfo);
 
-            return new CertificateRequestMessage(CertReqMsg.GetInstance(new DerSequence(v)));
+            return new CertificateRequestMessage(certReqMsg);
         }
 
         private static Time CreateTime(DateTime? dateTime)
diff --git a/crypto/src/crmf/CertificateResponse.cs b/crypto/src/crmf/CertificateResponse.cs
new file mode 100644
index 000000000..3a3f0d7c6
--- /dev/null
+++ b/crypto/src/crmf/CertificateResponse.cs
@@ -0,0 +1,94 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Pkcs;
+using Org.BouncyCastle.Cms;
+
+namespace Org.BouncyCastle.Crmf
+{
+    /// <summary>High level wrapper for the CertResponse CRMF structure.</summary>
+    public class CertificateResponse
+    {
+        private readonly CertResponse m_certResponse;
+
+        public CertificateResponse(CertResponse certResponse)
+        {
+            m_certResponse = certResponse;
+        }
+
+        /**
+         * Return true if the response contains an encrypted certificate.
+         *
+         * @return true if certificate in response encrypted, false otherwise.
+         */
+        public virtual bool HasEncryptedCertificate =>
+            m_certResponse.CertifiedKeyPair.CertOrEncCert.HasEncryptedCertificate;
+
+        /**
+         * Return a CMSEnvelopedData representing the encrypted certificate contained in the response.
+         *
+         * @return a CMEEnvelopedData if an encrypted certificate is present.
+         * @throws IllegalStateException if no encrypted certificate is present, or there is an issue with the enveloped data.
+         */
+        public virtual CmsEnvelopedData GetEncryptedCertificate()
+        {
+            if (!HasEncryptedCertificate)
+                throw new InvalidOperationException("encrypted certificate asked for, none found");
+
+            CertifiedKeyPair receivedKeyPair = m_certResponse.CertifiedKeyPair;
+
+            var contentInfo = new Asn1.Cms.ContentInfo(PkcsObjectIdentifiers.EnvelopedData,
+                receivedKeyPair.CertOrEncCert.EncryptedCert.Value);
+
+            CmsEnvelopedData envelopedData = new CmsEnvelopedData(contentInfo);
+
+            if (envelopedData.GetRecipientInfos().Count != 1)
+                throw new InvalidOperationException("data encrypted for more than one recipient");
+
+            return envelopedData;
+        }
+
+        // TODO[crmf]
+#if false
+        /**
+         * Return the CMPCertificate representing the plaintext certificate in the response.
+         *
+         * @return a CMPCertificate if a plaintext certificate is present.
+         * @throws IllegalStateException if no plaintext certificate is present.
+         */
+        public virtual CmpCertificate GetCertificate(Recipient recipient)
+        {
+            CmsEnvelopedData encryptedCert = GetEncryptedCertificate();
+
+            RecipientInformationStore recipients = encryptedCert.GetRecipientInfos();
+
+            var c = recipients.GetRecipients();
+
+            RecipientInformation recInfo = c[0];
+
+            return CmpCertificate.GetInstance(recInfo.GetContent(recipient));
+        }
+#endif
+
+        /**
+         * Return the CMPCertificate representing the plaintext certificate in the response.
+         *
+         * @return a CMPCertificate if a plaintext certificate is present.
+         * @throws IllegalStateException if no plaintext certificate is present.
+         */
+        public virtual CmpCertificate GetCertificate()
+        {
+            if (HasEncryptedCertificate)
+                throw new InvalidOperationException("plaintext certificate asked for, none found");
+
+            return m_certResponse.CertifiedKeyPair.CertOrEncCert.Certificate;
+        }
+
+        /**
+         * Return this object's underlying ASN.1 structure.
+         *
+         * @return a CertResponse
+         */
+        public virtual CertResponse ToAsn1Structure() => m_certResponse;
+    }
+}
diff --git a/crypto/src/crmf/CertificateResponseBuilder.cs b/crypto/src/crmf/CertificateResponseBuilder.cs
new file mode 100644
index 000000000..d1e85ea23
--- /dev/null
+++ b/crypto/src/crmf/CertificateResponseBuilder.cs
@@ -0,0 +1,104 @@
+using System;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Cms;
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Cms;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Crmf
+{
+    /// <summary>Builder for CertificateResponse objects (the CertResponse CRMF equivalent).</summary>
+    public class CertificateResponseBuilder
+    {
+        private readonly DerInteger m_certReqID;
+        private readonly PkiStatusInfo m_statusInfo;
+
+        private CertifiedKeyPair m_certKeyPair;
+        private Asn1OctetString m_rspInfo;
+
+        /**
+         * Base constructor.
+         *
+         * @param certReqId the request ID for the response.
+         * @param statusInfo the status info to associate with the response.
+         */
+        public CertificateResponseBuilder(DerInteger certReqID, PkiStatusInfo statusInfo)
+        {
+            m_certReqID = certReqID;
+            m_statusInfo = statusInfo;
+        }
+
+        /**
+         * Specify the certificate to assign to this response (in plaintext).
+         *
+         * @param certificate the X.509 PK certificate to include.
+         * @return the current builder.
+         */
+        public virtual CertificateResponseBuilder WithCertificate(X509Certificate certificate)
+        {
+            if (m_certKeyPair != null)
+                throw new InvalidOperationException("certificate in response already set");
+
+            var cmpCertificate = new CmpCertificate(certificate.CertificateStructure);
+
+            m_certKeyPair = new CertifiedKeyPair(new CertOrEncCert(cmpCertificate));
+
+            return this;
+        }
+
+        /**
+         * Specify the certificate to assign to this response (in plaintext).
+         *
+         * @param cmpCertificate the X.509 PK certificate to include.
+         * @return the current builder.
+         */
+        public virtual CertificateResponseBuilder WithCertificate(CmpCertificate cmpCertificate)
+        {
+            if (m_certKeyPair != null)
+                throw new InvalidOperationException("certificate in response already set");
+
+            m_certKeyPair = new CertifiedKeyPair(new CertOrEncCert(cmpCertificate));
+
+            return this;
+        }
+
+        /**
+         * Specify the encrypted certificate to assign to this response (in plaintext).
+         *
+         * @param encryptedCertificate an encrypted
+         * @return the current builder.
+         */
+        public virtual CertificateResponseBuilder WithCertificate(CmsEnvelopedData encryptedCertificate)
+        {
+            if (m_certKeyPair != null)
+                throw new InvalidOperationException("certificate in response already set");
+
+            var encryptedKey = new EncryptedKey(EnvelopedData.GetInstance(encryptedCertificate.ContentInfo.Content));
+
+            m_certKeyPair = new CertifiedKeyPair(new CertOrEncCert(encryptedKey));
+
+            return this;
+        }
+
+        /**
+         * Specify the response info field on the response.
+         *
+         * @param responseInfo a response info string.
+         * @return the current builder.
+         */
+        public virtual CertificateResponseBuilder WithResponseInfo(byte[] responseInfo)
+        {
+            if (m_rspInfo != null)
+                throw new InvalidOperationException("response info already set");
+
+            m_rspInfo = new DerOctetString(responseInfo);
+
+            return this;
+        }
+
+        public virtual CertificateResponse Build() =>
+            new CertificateResponse(new CertResponse(m_certReqID, m_statusInfo, m_certKeyPair, m_rspInfo));
+    }
+}
diff --git a/crypto/src/crmf/EncryptedValueBuilder.cs b/crypto/src/crmf/EncryptedValueBuilder.cs
index 3dca9e72c..a056aadf6 100644
--- a/crypto/src/crmf/EncryptedValueBuilder.cs
+++ b/crypto/src/crmf/EncryptedValueBuilder.cs
@@ -146,14 +146,6 @@ namespace Org.BouncyCastle.Crmf
             return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, encValue);
         }
 
-        private byte[] PadData(byte[] data)
-        {
-            if (padder != null)
-            {
-                return padder.GetPaddedData(data);
-            }
-
-            return data;
-        }
+        private byte[] PadData(byte[] data) => padder?.GetPaddedData(data) ?? data;
     }
 }
diff --git a/crypto/src/crmf/EncryptedValueParser.cs b/crypto/src/crmf/EncryptedValueParser.cs
new file mode 100644
index 000000000..6be4cbadf
--- /dev/null
+++ b/crypto/src/crmf/EncryptedValueParser.cs
@@ -0,0 +1,97 @@
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+
+namespace Org.BouncyCastle.Crmf
+{
+    /// <summary>Parser for EncryptedValue structures.</summary>
+    public class EncryptedValueParser
+    {
+        private readonly EncryptedValue m_value;
+        private readonly IEncryptedValuePadder m_padder;
+
+        /**
+         * Basic constructor - create a parser to read the passed in value.
+         *
+         * @param value the value to be parsed.
+         */
+        public EncryptedValueParser(EncryptedValue value)
+            : this(value, null)
+        {
+        }
+
+        /**
+         * Create a parser to read the passed in value, assuming the padder was
+         * applied to the data prior to encryption.
+         *
+         * @param value  the value to be parsed.
+         * @param padder the padder to be used to remove padding from the decrypted value..
+         */
+        public EncryptedValueParser(EncryptedValue value, IEncryptedValuePadder padder)
+        {
+            m_value = value;
+            m_padder = padder;
+        }
+
+        public virtual AlgorithmIdentifier IntendedAlg => m_value.IntendedAlg;
+
+        // TODO[crmf]
+#if false
+        private virtual byte[] DecryptValue(ValueDecryptorGenerator decGen)
+        {
+            if (m_value.ValueHint != null)
+                throw new NotSupportedException();
+
+            InputDecryptor decryptor = decGen.getValueDecryptor(value.getKeyAlg(),
+                value.getSymmAlg(), value.getEncSymmKey().getBytes());
+            InputStream dataIn = decryptor.getInputStream(new ByteArrayInputStream(
+                value.getEncValue().getBytes()));
+            try
+            {
+                return UnpadData(Streams.readAll(dataIn));
+            }
+            catch (IOException e)
+            {
+                throw new CRMFException("Cannot parse decrypted data: " + e.getMessage(), e);
+            }
+        }
+
+        /**
+         * Read a X.509 certificate.
+         *
+         * @param decGen the decryptor generator to decrypt the encrypted value.
+         * @return an X509CertificateHolder containing the certificate read.
+         * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+         */
+        public virtual X509Certificate ReadCertificate(ValueDecryptorGenerator decGen)
+        {
+            return new X509Certificate(X509CertificateStructure.GetInstance(DecryptValue(decGen)));
+        }
+
+        /**
+         * Read a PKCS#8 PrivateKeyInfo.
+         *
+         * @param decGen the decryptor generator to decrypt the encrypted value.
+         * @return an PrivateKeyInfo containing the private key that was read.
+         * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+         */
+        public virtual PrivateKeyInfo ReadPrivateKeyInfo(ValueDecryptorGenerator decGen)
+        {
+            return PrivateKeyInfo.GetInstance(DecryptValue(decGen));
+        }
+
+        /**
+         * Read a pass phrase.
+         *
+         * @param decGen the decryptor generator to decrypt the encrypted value.
+         * @return a pass phrase as recovered from the encrypted value.
+         * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+         */
+        public virtual char[] ReadPassphrase(ValueDecryptorGenerator decGen)
+        {
+            return Strings.FromUtf8ByteArray(DecryptValue(decGen)).ToCharArray();
+        }
+#endif
+
+        private byte[] UnpadData(byte[] data) => m_padder?.GetUnpaddedData(data) ?? data;
+    }
+}
diff --git a/crypto/src/crmf/PKMacBuilder.cs b/crypto/src/crmf/PKMacBuilder.cs
index f59ba8f35..ac9e7ca18 100644
--- a/crypto/src/crmf/PKMacBuilder.cs
+++ b/crypto/src/crmf/PKMacBuilder.cs
@@ -52,8 +52,10 @@ namespace Org.BouncyCastle.Crmf
         /// <summary>
         /// Default, IterationCount = 1000, OIW=IdSha1, Mac=HmacSHA1
         /// </summary>
-        public PKMacBuilder() :
-            this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), new DefaultPKMacPrimitivesProvider())
+        public PKMacBuilder()
+            :   this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000,
+                    new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance),
+                    new DefaultPKMacPrimitivesProvider())
         {
         }
 
@@ -61,8 +63,9 @@ namespace Org.BouncyCastle.Crmf
         /// Defaults with IPKMacPrimitivesProvider
         /// </summary>
         /// <param name="provider"></param>
-        public PKMacBuilder(IPKMacPrimitivesProvider provider) :
-            this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000, new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), provider)
+        public PKMacBuilder(IPKMacPrimitivesProvider provider)
+            :   this(new AlgorithmIdentifier(OiwObjectIdentifiers.IdSha1), 1000,
+                    new AlgorithmIdentifier(IanaObjectIdentifiers.HmacSha1, DerNull.Instance), provider)
         {
         }
 
@@ -72,8 +75,9 @@ namespace Org.BouncyCastle.Crmf
         /// <param name="provider">The Mac provider</param>
         /// <param name="digestAlgorithmIdentifier">Digest Algorithm Id</param>
         /// <param name="macAlgorithmIdentifier">Mac Algorithm Id</param>
-        public PKMacBuilder(IPKMacPrimitivesProvider provider, AlgorithmIdentifier digestAlgorithmIdentifier, AlgorithmIdentifier macAlgorithmIdentifier) :
-            this(digestAlgorithmIdentifier, 1000, macAlgorithmIdentifier, provider)
+        public PKMacBuilder(IPKMacPrimitivesProvider provider, AlgorithmIdentifier digestAlgorithmIdentifier,
+            AlgorithmIdentifier macAlgorithmIdentifier)
+            : this(digestAlgorithmIdentifier, 1000, macAlgorithmIdentifier, provider)
         {
         }
 
@@ -88,7 +92,8 @@ namespace Org.BouncyCastle.Crmf
             this.maxIterations = maxIterations;
         }
 
-        private PKMacBuilder(AlgorithmIdentifier digestAlgorithmIdentifier, int iterationCount, AlgorithmIdentifier macAlgorithmIdentifier, IPKMacPrimitivesProvider provider)
+        private PKMacBuilder(AlgorithmIdentifier digestAlgorithmIdentifier, int iterationCount,
+            AlgorithmIdentifier macAlgorithmIdentifier, IPKMacPrimitivesProvider provider)
         {
             this.iterationCount = iterationCount;
             this.mac = macAlgorithmIdentifier;
@@ -131,6 +136,18 @@ namespace Org.BouncyCastle.Crmf
         }
 
         /// <summary>
+        /// The Secure random
+        /// </summary>
+        /// <param name="random">The random.</param>
+        /// <returns>this</returns>
+        public PKMacBuilder SetSecureRandom(SecureRandom random)
+        {
+            this.random = random;
+
+            return this;
+        }
+
+        /// <summary>
         /// Set PbmParameters
         /// </summary>
         /// <param name="parameters">The parameters.</param>
@@ -144,18 +161,32 @@ namespace Org.BouncyCastle.Crmf
             return this;
         }
 
-        /// <summary>
-        /// The Secure random
-        /// </summary>
-        /// <param name="random">The random.</param>
-        /// <returns>this</returns>
-        public PKMacBuilder SetSecureRandom(SecureRandom random)
+        public IMacFactory Get(AlgorithmIdentifier algorithm, char[] password)
         {
-            this.random = random;
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+            return Get(algorithm, password.AsSpan());
+#else
+            if (!CmpObjectIdentifiers.passwordBasedMac.Equals(algorithm.Algorithm))
+                throw new ArgumentException("protection algorithm not mac based", nameof(algorithm));
 
-            return this;
+            SetParameters(PbmParameter.GetInstance(algorithm.Parameters));
+
+            return Build(password);
+#endif
         }
 
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        public IMacFactory Get(AlgorithmIdentifier algorithm, ReadOnlySpan<char> password)
+        {
+            if (!CmpObjectIdentifiers.passwordBasedMac.Equals(algorithm.Algorithm))
+                throw new ArgumentException("protection algorithm not mac based", nameof(algorithm));
+
+            SetParameters(PbmParameter.GetInstance(algorithm.Parameters));
+
+            return Build(password);
+        }
+#endif
+
         /// <summary>
         /// Build an IMacFactory.
         /// </summary>
diff --git a/crypto/src/crmf/PKMacValueGenerator.cs b/crypto/src/crmf/PKMacValueGenerator.cs
new file mode 100644
index 000000000..0809de348
--- /dev/null
+++ b/crypto/src/crmf/PKMacValueGenerator.cs
@@ -0,0 +1,28 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Crmf
+{
+    internal static class PKMacValueGenerator
+    {
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal static PKMacValue Generate(PKMacBuilder builder, ReadOnlySpan<char> password,
+            SubjectPublicKeyInfo keyInfo)
+        {
+            var macFactory = builder.Build(password);
+            var macValue = X509Utilities.GenerateMac(macFactory, keyInfo);
+            return new PKMacValue((AlgorithmIdentifier)macFactory.AlgorithmDetails, macValue);
+        }
+#else
+        internal static PKMacValue Generate(PKMacBuilder builder, char[] password, SubjectPublicKeyInfo keyInfo)
+        {
+            var macFactory = builder.Build(password);
+            var macValue = X509Utilities.GenerateMac(macFactory, keyInfo);
+            return new PKMacValue((AlgorithmIdentifier)macFactory.AlgorithmDetails, macValue);
+        }
+#endif
+    }
+}
diff --git a/crypto/src/crmf/PKMacValueVerifier.cs b/crypto/src/crmf/PKMacValueVerifier.cs
new file mode 100644
index 000000000..0f84e64e1
--- /dev/null
+++ b/crypto/src/crmf/PKMacValueVerifier.cs
@@ -0,0 +1,39 @@
+using System;
+
+using Org.BouncyCastle.Asn1.Cmp;
+using Org.BouncyCastle.Asn1.Crmf;
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.X509;
+
+namespace Org.BouncyCastle.Crmf
+{
+    internal class PKMacValueVerifier
+    {
+        private readonly PKMacBuilder m_builder;
+
+        internal PKMacValueVerifier(PKMacBuilder builder)
+        {
+            m_builder = builder;
+        }
+
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+        internal virtual bool IsValid(PKMacValue value, ReadOnlySpan<char> password, SubjectPublicKeyInfo keyInfo)
+        {
+            m_builder.SetParameters(PbmParameter.GetInstance(value.AlgID.Parameters));
+
+            var macFactory = m_builder.Build(password);
+
+            return X509Utilities.VerifyMac(macFactory, keyInfo, value.MacValue);
+        }
+#else
+        internal virtual bool IsValid(PKMacValue value, char[] password, SubjectPublicKeyInfo keyInfo)
+        {
+            m_builder.SetParameters(PbmParameter.GetInstance(value.AlgID.Parameters));
+
+            var macFactory = m_builder.Build(password);
+
+            return X509Utilities.VerifyMac(macFactory, keyInfo, value.MacValue);
+        }
+#endif
+    }
+}
diff --git a/crypto/src/crmf/PkiArchiveControl.cs b/crypto/src/crmf/PkiArchiveControl.cs
index 251b8db96..122c043cb 100644
--- a/crypto/src/crmf/PkiArchiveControl.cs
+++ b/crypto/src/crmf/PkiArchiveControl.cs
@@ -14,9 +14,7 @@ namespace Org.BouncyCastle.Crmf
         public static readonly int keyGenParameters = PkiArchiveOptions.keyGenParameters;
         public static readonly int archiveRemGenPrivKey = PkiArchiveOptions.archiveRemGenPrivKey;
 
-        private static readonly DerObjectIdentifier type = CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions;
-
-        private readonly PkiArchiveOptions pkiArchiveOptions;
+        private readonly PkiArchiveOptions m_pkiArchiveOptions;
 
         /// <summary>
         /// Basic constructor - build from an PKIArchiveOptions structure.
@@ -24,48 +22,42 @@ namespace Org.BouncyCastle.Crmf
         /// <param name="pkiArchiveOptions">the ASN.1 structure that will underlie this control.</param>
         public PkiArchiveControl(PkiArchiveOptions pkiArchiveOptions)
         {
-            this.pkiArchiveOptions = pkiArchiveOptions;
+            m_pkiArchiveOptions = pkiArchiveOptions;
         }
 
         /// <summary>
         /// Return the type of this control.
         /// </summary>
         /// <returns>CRMFObjectIdentifiers.id_regCtrl_pkiArchiveOptions</returns>
-        public DerObjectIdentifier Type
-        {
-
-            get { return type; }
-        }
+        public DerObjectIdentifier Type => CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions;
 
         /// <summary>
         /// Return the underlying ASN.1 object.
         /// </summary>
         /// <returns>a PKIArchiveOptions structure.</returns>    
-        public Asn1Encodable Value
-        {
-            get { return pkiArchiveOptions; }
-        }
+        public Asn1Encodable Value => m_pkiArchiveOptions;
 
         /// <summary>
         /// Return the archive control type, one of: encryptedPrivKey,keyGenParameters,or archiveRemGenPrivKey.
         /// </summary>
         /// <returns>the archive control type.</returns>
-        public int ArchiveType
-        {
-            get { return pkiArchiveOptions.Type; }
-        }
+        public int ArchiveType => m_pkiArchiveOptions.Type;
 
         /// <summary>
         /// Return whether this control contains enveloped data.
         /// </summary>
         /// <returns>true if the control contains enveloped data, false otherwise.</returns>
-        public bool EnvelopedData
+        [Obsolete("Use 'IsEnvelopedData' instead")]
+        public bool EnvelopedData => IsEnvelopedData();
+
+        /// <summary>
+        /// Return whether this control contains enveloped data.
+        /// </summary>
+        /// <returns>true if the control contains enveloped data, false otherwise.</returns>
+        public bool IsEnvelopedData()
         {
-            get
-            {
-                EncryptedKey encKey = EncryptedKey.GetInstance(pkiArchiveOptions.Value);
-                return !encKey.IsEncryptedValue;
-            }
+            EncryptedKey encKey = EncryptedKey.GetInstance(m_pkiArchiveOptions.Value);
+            return !encKey.IsEncryptedValue;
         }
 
         /// <summary>
@@ -76,8 +68,8 @@ namespace Org.BouncyCastle.Crmf
         {
             try
             {
-                EncryptedKey encKey = EncryptedKey.GetInstance(pkiArchiveOptions.Value);
-                EnvelopedData data = Org.BouncyCastle.Asn1.Cms.EnvelopedData.GetInstance(encKey.Value);
+                EncryptedKey encKey = EncryptedKey.GetInstance(m_pkiArchiveOptions.Value);
+                EnvelopedData data = Asn1.Cms.EnvelopedData.GetInstance(encKey.Value);
 
                 return new CmsEnvelopedData(new ContentInfo(CmsObjectIdentifiers.EnvelopedData, data));
             }
diff --git a/crypto/src/crmf/PkiArchiveControlBuilder.cs b/crypto/src/crmf/PkiArchiveControlBuilder.cs
index d79f3b5ed..8cfa4ef19 100644
--- a/crypto/src/crmf/PkiArchiveControlBuilder.cs
+++ b/crypto/src/crmf/PkiArchiveControlBuilder.cs
@@ -12,8 +12,8 @@ namespace Org.BouncyCastle.Crmf
 {
     public class PkiArchiveControlBuilder
     {
-        private CmsEnvelopedDataGenerator envGen;
-        private CmsProcessableByteArray keyContent;
+        private readonly CmsEnvelopedDataGenerator m_envGen;
+        private readonly CmsProcessableByteArray m_keyContent;
 
         /// <summary>
         ///Basic constructor - specify the contents of the PKIArchiveControl structure.
@@ -27,14 +27,14 @@ namespace Org.BouncyCastle.Crmf
 
             try
             {
-                this.keyContent = new CmsProcessableByteArray(CrmfObjectIdentifiers.id_ct_encKeyWithID, encKeyWithID.GetEncoded());
+                m_keyContent = new CmsProcessableByteArray(CrmfObjectIdentifiers.id_ct_encKeyWithID, encKeyWithID.GetEncoded());
             }
             catch (IOException e)
             {
                 throw new InvalidOperationException("unable to encode key and general name info", e);
             }
 
-            this.envGen = new CmsEnvelopedDataGenerator();
+            m_envGen = new CmsEnvelopedDataGenerator();
         }
 
         ///<summary>Add a recipient generator to this control.</summary>       
@@ -42,7 +42,7 @@ namespace Org.BouncyCastle.Crmf
         ///<returns>this builder object.</returns>       
         public PkiArchiveControlBuilder AddRecipientGenerator(RecipientInfoGenerator recipientGen)
         {
-            envGen.AddRecipientInfoGenerator(recipientGen);
+            m_envGen.AddRecipientInfoGenerator(recipientGen);
             return this;
         }
        
@@ -51,9 +51,26 @@ namespace Org.BouncyCastle.Crmf
         /// <returns>a PKIArchiveControl object.</returns>
         public PkiArchiveControl Build(ICipherBuilderWithKey contentEncryptor)
         {                                            
-            CmsEnvelopedData envContent = envGen.Generate(keyContent, contentEncryptor);
+            CmsEnvelopedData envContent = m_envGen.Generate(m_keyContent, contentEncryptor);
             EnvelopedData envD = EnvelopedData.GetInstance(envContent.ContentInfo.Content);        
             return new PkiArchiveControl(new PkiArchiveOptions(new EncryptedKey(envD)));
         }
+
+        // TODO[crmf]
+#if false
+        /**
+         * Build the PKIArchiveControl using the passed in encryptor to encrypt its contents.
+         *
+         * @param contentEncryptor a suitable content encryptor.
+         * @return a PKIArchiveControl object.
+         * @throws CMSException in the event the build fails.
+         */
+        public PkiArchiveControl Build(OutputEncryptor contentEncryptor)
+        {
+            CmsEnvelopedData envContent = m_envGen.Generate(m_keyContent, contentEncryptor);
+            EnvelopedData envD = EnvelopedData.GetInstance(envContent.ContentInfo.Content);
+            return new PkiArchiveControl(new PkiArchiveOptions(new EncryptedKey(envD)));
+        }
+#endif
     }
 }
diff --git a/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs b/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs
index 02af74924..bace00334 100644
--- a/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs
+++ b/crypto/src/crmf/ProofOfPossessionSigningKeyBuilder.cs
@@ -1,6 +1,4 @@
 using System;
-using System.IO;
-using System.Net.Security;
 
 using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Crmf;
@@ -11,64 +9,65 @@ namespace Org.BouncyCastle.Crmf
 {
     public class ProofOfPossessionSigningKeyBuilder
     {
-        private CertRequest _certRequest;
-        private SubjectPublicKeyInfo _pubKeyInfo;
-        private GeneralName _name;
-        private PKMacValue _publicKeyMAC;
+        private readonly CertRequest m_certRequest;
+        private readonly SubjectPublicKeyInfo m_pubKeyInfo;
+
+        private GeneralName m_name = null;
+        private PKMacValue m_publicKeyMac = null;
 
         public ProofOfPossessionSigningKeyBuilder(CertRequest certRequest)
         {
-            this._certRequest = certRequest;
+            m_certRequest = certRequest;
+            m_pubKeyInfo = null;
         }
 
         public ProofOfPossessionSigningKeyBuilder(SubjectPublicKeyInfo pubKeyInfo)
         {
-            this._pubKeyInfo = pubKeyInfo;
+            m_certRequest = null;
+            m_pubKeyInfo = pubKeyInfo;
         }
 
         public ProofOfPossessionSigningKeyBuilder SetSender(GeneralName name)
         {
-            this._name = name;
+            m_name = name;
             return this;
         }
 
         public ProofOfPossessionSigningKeyBuilder SetPublicKeyMac(PKMacBuilder generator, char[] password)
         {
-            IMacFactory fact = generator.Build(password);
-
-            return ImplSetPublicKeyMac(fact);
+            m_publicKeyMac = PKMacValueGenerator.Generate(generator, password, m_pubKeyInfo);
+            return this;
         }
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
         public ProofOfPossessionSigningKeyBuilder SetPublicKeyMac(PKMacBuilder generator, ReadOnlySpan<char> password)
         {
-            IMacFactory fact = generator.Build(password);
-
-            return ImplSetPublicKeyMac(fact);
+            m_publicKeyMac = PKMacValueGenerator.Generate(generator, password, m_pubKeyInfo);
+            return this;
         }
 #endif
 
         public PopoSigningKey Build(ISignatureFactory signer)
         {
-            if (_name != null && _publicKeyMAC != null)
+            if (m_name != null && m_publicKeyMac != null)
                 throw new InvalidOperationException("name and publicKeyMAC cannot both be set.");
 
             PopoSigningKeyInput popo;
             Asn1Encodable asn1Encodable;
 
-            if (_certRequest != null)
+            if (m_certRequest != null)
             {
                 popo = null;
-                asn1Encodable = _certRequest;
+                asn1Encodable = m_certRequest;
             }
-            else if (_name != null)
+            else if (m_name != null)
             {
-                popo = new PopoSigningKeyInput(_name, _pubKeyInfo);
+                popo = new PopoSigningKeyInput(m_name, m_pubKeyInfo);
                 asn1Encodable = popo;
             }
             else
             {
-                popo = new PopoSigningKeyInput(_publicKeyMAC, _pubKeyInfo);
+                popo = new PopoSigningKeyInput(m_publicKeyMac, m_pubKeyInfo);
                 asn1Encodable = popo;
             }
 
@@ -76,12 +75,5 @@ namespace Org.BouncyCastle.Crmf
 
             return new PopoSigningKey(popo, (AlgorithmIdentifier)signer.AlgorithmDetails, signature);
         }
-
-        private ProofOfPossessionSigningKeyBuilder ImplSetPublicKeyMac(IMacFactory macFactory)
-        {
-            var macValue = X509.X509Utilities.GenerateMac(macFactory, _pubKeyInfo);
-            this._publicKeyMAC = new PKMacValue((AlgorithmIdentifier)macFactory.AlgorithmDetails, macValue);
-            return this;
-        }
     }
 }
diff --git a/crypto/src/crmf/RegTokenControl.cs b/crypto/src/crmf/RegTokenControl.cs
index 43484097c..a890c289c 100644
--- a/crypto/src/crmf/RegTokenControl.cs
+++ b/crypto/src/crmf/RegTokenControl.cs
@@ -1,6 +1,4 @@
-using System;
-
-using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Asn1;
 using Org.BouncyCastle.Asn1.Crmf;
 
 namespace Org.BouncyCastle.Crmf
@@ -8,9 +6,7 @@ namespace Org.BouncyCastle.Crmf
     public class RegTokenControl
         : IControl
     {
-        private static readonly DerObjectIdentifier type = CrmfObjectIdentifiers.id_regCtrl_regToken;
-
-        private readonly DerUtf8String token;
+        private readonly DerUtf8String m_token;
 
         /// <summary>
         /// Basic constructor - build from a UTF-8 string representing the token.
@@ -18,7 +14,7 @@ namespace Org.BouncyCastle.Crmf
         /// <param name="token">UTF-8 string representing the token.</param>
         public RegTokenControl(DerUtf8String token)
         {
-            this.token = token;
+            m_token = token;
         }
 
         /// <summary>
@@ -27,25 +23,19 @@ namespace Org.BouncyCastle.Crmf
         /// <param name="token">string representing the token.</param>
         public RegTokenControl(string token)
         {
-            this.token = new DerUtf8String(token);
+            m_token = new DerUtf8String(token);
         }
 
         /// <summary>
         /// Return the type of this control.
         /// </summary>
         /// <returns>CRMFObjectIdentifiers.id_regCtrl_regToken</returns>
-        public DerObjectIdentifier Type
-        {
-            get { return type; }
-        }
+        public DerObjectIdentifier Type => CrmfObjectIdentifiers.id_regCtrl_regToken;
 
         /// <summary>
         /// Return the token associated with this control (a UTF8String).
         /// </summary>
         /// <returns>a UTF8String.</returns>
-        public Asn1Encodable Value
-        {
-            get { return token; }
-        }
+        public Asn1Encodable Value => m_token;
     }
 }