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.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", nameof(pkiMessage));
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", nameof(pkiMessage));
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 =>
CmpObjectIdentifiers.passwordBasedMac.Equals(ProtectionAlgorithm.Algorithm);
/**
* Return the message's protection algorithm.
*
* @return the algorithm ID for the message's protection algorithm.
*/
public virtual AlgorithmIdentifier ProtectionAlgorithm => m_pkiMessage.Header.ProtectionAlg;
///
/// 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];
return Array.ConvertAll(certs, cmpCertificate => new X509Certificate(cmpCertificate.X509v3PKCert));
}
///
/// 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) =>
X509Utilities.VerifySignature(verifierFactory, CreateProtected(), m_pkiMessage.Protection);
///
/// 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 NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
return Verify(pkMacBuilder, password.AsSpan());
#else
var protectionAlgorithm = ProtectionAlgorithm;
if (!CmpObjectIdentifiers.passwordBasedMac.Equals(protectionAlgorithm.Algorithm))
throw new InvalidOperationException("protection algorithm is not mac based");
PbmParameter parameter = PbmParameter.GetInstance(protectionAlgorithm.Parameters);
pkMacBuilder.SetParameters(parameter);
var macFactory = pkMacBuilder.Build(password);
return X509Utilities.VerifyMac(macFactory, CreateProtected(), m_pkiMessage.Protection);
#endif
}
#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
public virtual bool Verify(PKMacBuilder pkMacBuilder, ReadOnlySpan password)
{
var protectionAlgorithm = ProtectionAlgorithm;
if (!CmpObjectIdentifiers.passwordBasedMac.Equals(protectionAlgorithm.Algorithm))
throw new InvalidOperationException("protection algorithm is not mac based");
PbmParameter parameter = PbmParameter.GetInstance(protectionAlgorithm.Parameters);
pkMacBuilder.SetParameters(parameter);
var macFactory = pkMacBuilder.Build(password);
return X509Utilities.VerifyMac(macFactory, CreateProtected(), m_pkiMessage.Protection);
}
#endif
private DerSequence CreateProtected() => new DerSequence(m_pkiMessage.Header, m_pkiMessage.Body);
}
}