diff options
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; } } |