using System; using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1.Crmf; using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Operators; namespace Org.BouncyCastle.Crmf { public class CertificateRequestMessage { 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 m_certReqMsg; private readonly Controls m_controls; private static CertReqMsg ParseBytes(byte[] encoding) => CertReqMsg.GetInstance(encoding); /// /// Create a CertificateRequestMessage from the passed in bytes. /// /// BER/DER encoding of the CertReqMsg structure. public CertificateRequestMessage(byte[] encoded) : this(ParseBytes(encoded)) { } public CertificateRequestMessage(CertReqMsg certReqMsg) { m_certReqMsg = certReqMsg; m_controls = certReqMsg.CertReq.Controls; } /// /// Return the underlying ASN.1 object defining this CertificateRequestMessage object. /// /// A CertReqMsg public CertReqMsg ToAsn1Structure() => m_certReqMsg; /// /// Return the certificate request ID for this message. /// /// the certificate request ID. public DerInteger GetCertReqID() => m_certReqMsg.CertReq.CertReqID; /// /// Return the certificate template contained in this message. /// /// a CertTemplate structure. public CertTemplate GetCertTemplate() => m_certReqMsg.CertReq.CertTemplate; /// /// Return whether or not this request has control values associated with it. /// /// true if there are control values present, false otherwise. public bool HasControls => m_controls != null; /// /// Return whether or not this request has a specific type of control value. /// /// the type OID for the control value we are checking for. /// true if a control value of type is present, false otherwise. public bool HasControl(DerObjectIdentifier objectIdentifier) => FindControl(objectIdentifier) != null; /// /// Return a control value of the specified type. /// /// the type OID for the control value we are checking for. /// the control value if present, null otherwise. public IControl GetControl(DerObjectIdentifier type) { AttributeTypeAndValue found = FindControl(type); if (found != null) { var oid = found.Type; if (CrmfObjectIdentifiers.id_regCtrl_pkiArchiveOptions.Equals(oid)) return new PkiArchiveControl(PkiArchiveOptions.GetInstance(found.Value)); if (CrmfObjectIdentifiers.id_regCtrl_regToken.Equals(oid)) return new RegTokenControl(DerUtf8String.GetInstance(found.Value)); if (CrmfObjectIdentifiers.id_regCtrl_authenticator.Equals(oid)) return new AuthenticatorControl(DerUtf8String.GetInstance(found.Value)); } return null; } public AttributeTypeAndValue FindControl(DerObjectIdentifier type) { if (m_controls == null) return null; AttributeTypeAndValue[] tAndV = m_controls.ToAttributeTypeAndValueArray(); AttributeTypeAndValue found = null; for (int i = 0; i < tAndV.Length; i++) { if (tAndV[i].Type.Equals(type)) { found = tAndV[i]; break; } } return found; } /// /// Return whether or not this request message has a proof-of-possession field in it. /// /// true if proof-of-possession is present, false otherwise. public bool HasProofOfPossession => m_certReqMsg.Pop != null; /// /// Return the type of the proof-of-possession this request message provides. /// /// one of: popRaVerified, popSigningKey, popKeyEncipherment, popKeyAgreement public int ProofOfPossession => m_certReqMsg.Pop.Type; /// /// Return whether or not the proof-of-possession (POP) is of the type popSigningKey and /// it has a public key MAC associated with it. /// /// true if POP is popSigningKey and a PKMAC is present, false otherwise. public bool HasSigningKeyProofOfPossessionWithPkMac { get { ProofOfPossession pop = m_certReqMsg.Pop; if (pop.Type != popSigningKey) return false; PopoSigningKey popoSign = PopoSigningKey.GetInstance(pop.Object); return popoSign.PoposkInput.PublicKeyMac != null; } } /// /// Return whether or not a signing key proof-of-possession (POP) is valid. /// /// a provider that can produce content verifiers for the signature contained in this POP. /// true if the POP is valid, false otherwise. /// if there is a problem in verification or content verifier creation. /// if POP not appropriate. public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider) { 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.PublicKeyMac != null) throw new InvalidOperationException("verification requires password check"); return VerifySignature(verifierProvider, popoSign); } /// /// Return whether or not a signing key proof-of-possession (POP), with an associated PKMAC, is valid. /// /// a provider that can produce content verifiers for the signature contained in this POP. /// a suitable PKMacBuilder to create the MAC verifier. /// the password used to key the MAC calculation. /// true if the POP is valid, false otherwise. /// if there is a problem in verification or content verifier creation. /// if POP not appropriate. 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 /// /// Return whether or not a signing key proof-of-possession (POP), with an associated PKMAC, is valid. /// /// a provider that can produce content verifiers for the signature contained in this POP. /// a suitable PKMacBuilder to create the MAC verifier. /// the password used to key the MAC calculation. /// true if the POP is valid, false otherwise. /// if there is a problem in verification or content verifier creation. /// if POP not appropriate. public bool IsValidSigningKeyPop(IVerifierFactoryProvider verifierProvider, PKMacBuilder macBuilder, ReadOnlySpan 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); Asn1Encodable asn1Encodable = signKey.PoposkInput; if (asn1Encodable == null) { asn1Encodable = m_certReqMsg.CertReq; } return X509.X509Utilities.VerifySignature(verifierFactory, asn1Encodable, signKey.Signature); } /// /// Return the ASN.1 encoding of the certReqMsg we wrap. /// /// a byte array containing the binary encoding of the certReqMsg. public byte[] GetEncoded() => m_certReqMsg.GetEncoded(); } }