using System; 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.Utilities; using Org.BouncyCastle.X509; namespace Org.BouncyCastle.Cmp { /// /// Wrapper for a PKIMessage with protection attached to it. /// public class ProtectedPkiMessage { private readonly PkiMessage m_pkiMessage; /// /// Wrap a general message. /// /// If the general message does not have protection. /// The General message public ProtectedPkiMessage(GeneralPkiMessage pkiMessage) { if (!pkiMessage.HasProtection) throw new ArgumentException("GeneralPkiMessage not protected"); this.m_pkiMessage = pkiMessage.ToAsn1Structure(); } // TODO[cmp] Make internal? (Has test that uses it) /// /// Wrap a PKI message. /// /// If the PKI message does not have protection. /// The PKI message public ProtectedPkiMessage(PkiMessage pkiMessage) { if (null == pkiMessage.Header.ProtectionAlg) throw new ArgumentException("PkiMessage not protected"); this.m_pkiMessage = pkiMessage; } /// Message header public virtual PkiHeader Header => m_pkiMessage.Header; /// Message body public virtual PkiBody Body => m_pkiMessage.Body; /// /// Return the underlying ASN.1 structure contained in this object. /// /// PkiMessage structure public virtual PkiMessage ToAsn1Message() => m_pkiMessage; /// /// Determine whether the message is protected by a password based MAC. Use verify(PKMACBuilder, char[]) /// to verify the message if this method returns true. /// /// true if protection MAC PBE based, false otherwise. public virtual bool HasPasswordBasedMacProtected { get { return CmpObjectIdentifiers.passwordBasedMac.Equals(Header.ProtectionAlg.Algorithm); } } /// /// Return the extra certificates associated with this message. /// /// an array of extra certificates, zero length if none present. public virtual X509Certificate[] GetCertificates() { CmpCertificate[] certs = m_pkiMessage.GetExtraCerts(); if (null == certs) return new X509Certificate[0]; X509Certificate[] result = new X509Certificate[certs.Length]; for (int t = 0; t < certs.Length; t++) { result[t] = new X509Certificate(certs[t].X509v3PKCert); } return result; } /// /// Verify a message with a public key based signature attached. /// /// a factory of signature verifiers. /// true if the provider is able to create a verifier that validates the signature, false otherwise. public virtual bool Verify(IVerifierFactory verifierFactory) { IStreamCalculator streamCalculator = verifierFactory.CreateCalculator(); IVerifier result = Process(streamCalculator); return result.IsVerified(m_pkiMessage.Protection.GetBytes()); } /// /// Verify a message with password based MAC protection. /// /// MAC builder that can be used to construct the appropriate MacCalculator /// the MAC password /// true if the passed in password and MAC builder verify the message, false otherwise. /// if algorithm not MAC based, or an exception is thrown verifying the MAC. public virtual bool Verify(PKMacBuilder pkMacBuilder, char[] password) { if (!CmpObjectIdentifiers.passwordBasedMac.Equals(m_pkiMessage.Header.ProtectionAlg.Algorithm)) throw new InvalidOperationException("protection algorithm is not mac based"); PbmParameter parameter = PbmParameter.GetInstance(m_pkiMessage.Header.ProtectionAlg.Parameters); pkMacBuilder.SetParameters(parameter); IBlockResult result = Process(pkMacBuilder.Build(password).CreateCalculator()); return Arrays.FixedTimeEquals(result.Collect(), m_pkiMessage.Protection.GetBytes()); } #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER public virtual bool Verify(PKMacBuilder pkMacBuilder, ReadOnlySpan password) { if (!CmpObjectIdentifiers.passwordBasedMac.Equals(m_pkiMessage.Header.ProtectionAlg.Algorithm)) throw new InvalidOperationException("protection algorithm is not mac based"); PbmParameter parameter = PbmParameter.GetInstance(m_pkiMessage.Header.ProtectionAlg.Parameters); pkMacBuilder.SetParameters(parameter); IBlockResult result = Process(pkMacBuilder.Build(password).CreateCalculator()); return Arrays.FixedTimeEquals(result.Collect(), m_pkiMessage.Protection.GetBytes()); } #endif private TResult Process(IStreamCalculator streamCalculator) { Asn1EncodableVector avec = new Asn1EncodableVector(); avec.Add(m_pkiMessage.Header); avec.Add(m_pkiMessage.Body); byte[] enc = new DerSequence(avec).GetDerEncoded(); using (var stream = streamCalculator.Stream) { stream.Write(enc, 0, enc.Length); } return streamCalculator.GetResult(); } } }