SignerInformation
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Cms;
using Org.BouncyCastle.Asn1.EdEC;
using Org.BouncyCastle.Asn1.Nist;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.IO;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.Utilities.Collections;
using Org.BouncyCastle.X509;
using System;
using System.Collections.Generic;
using System.IO;
namespace Org.BouncyCastle.Cms
{
public class SignerInformation
{
private SignerID sid;
private CmsProcessable content;
private byte[] signature;
private DerObjectIdentifier contentType;
private byte[] calculatedDigest;
private byte[] resultDigest;
private Org.BouncyCastle.Asn1.Cms.AttributeTable signedAttributeTable;
private Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttributeTable;
private readonly bool isCounterSignature;
protected Org.BouncyCastle.Asn1.Cms.SignerInfo info;
protected AlgorithmIdentifier digestAlgorithm;
protected AlgorithmIdentifier encryptionAlgorithm;
protected readonly Asn1Set signedAttributeSet;
protected readonly Asn1Set unsignedAttributeSet;
public bool IsCounterSignature => isCounterSignature;
public DerObjectIdentifier ContentType => contentType;
public SignerID SignerID => sid;
public int Version => info.Version.IntValueExact;
public AlgorithmIdentifier DigestAlgorithmID => digestAlgorithm;
[Obsolete("Use 'DigestAlgorithmID' instead")]
public string DigestAlgOid {
get {
return digestAlgorithm.Algorithm.GetID();
}
}
[Obsolete("Use 'DigestAlgorithmID' instead")]
public Asn1Object DigestAlgParams {
get {
return digestAlgorithm.Parameters?.ToAsn1Object();
}
}
[Obsolete("Use 'SignatureAlgorithm' property instead")]
public AlgorithmIdentifier EncryptionAlgorithmID {
get {
return encryptionAlgorithm;
}
}
[Obsolete("Use 'SignatureAlgorithm' property instead")]
public string EncryptionAlgOid {
get {
return encryptionAlgorithm.Algorithm.Id;
}
}
[Obsolete("Use 'SignatureAlgorithm' property instead")]
public Asn1Object EncryptionAlgParams {
get {
return encryptionAlgorithm.Parameters?.ToAsn1Object();
}
}
public AlgorithmIdentifier SignatureAlgorithm => encryptionAlgorithm;
public Org.BouncyCastle.Asn1.Cms.AttributeTable SignedAttributes {
get {
if (signedAttributeTable == null) {
Asn1Set asn1Set = signedAttributeSet;
signedAttributeTable = ((asn1Set != null) ? asn1Set.ToAttributeTable() : null);
}
return signedAttributeTable;
}
}
public Org.BouncyCastle.Asn1.Cms.SignerInfo SignerInfo => info;
public Org.BouncyCastle.Asn1.Cms.AttributeTable UnsignedAttributes {
get {
if (unsignedAttributeTable == null) {
Asn1Set asn1Set = unsignedAttributeSet;
unsignedAttributeTable = ((asn1Set != null) ? asn1Set.ToAttributeTable() : null);
}
return unsignedAttributeTable;
}
}
internal SignerInformation(Org.BouncyCastle.Asn1.Cms.SignerInfo info, DerObjectIdentifier contentType, CmsProcessable content, byte[] calculatedDigest)
{
this.info = info;
sid = new SignerID();
this.contentType = contentType;
isCounterSignature = (contentType == null);
try {
SignerIdentifier signerID = info.SignerID;
if (signerID.IsTagged) {
Asn1OctetString asn1OctetString = (Asn1OctetString)signerID.ID;
sid.SubjectKeyIdentifier = asn1OctetString.GetEncoded("DER");
} else {
Org.BouncyCastle.Asn1.Cms.IssuerAndSerialNumber instance = Org.BouncyCastle.Asn1.Cms.IssuerAndSerialNumber.GetInstance(signerID.ID);
sid.Issuer = instance.Issuer;
sid.SerialNumber = instance.SerialNumber.Value;
}
} catch (IOException) {
throw new ArgumentException("invalid sid in SignerInfo");
}
digestAlgorithm = info.DigestAlgorithm;
signedAttributeSet = info.SignedAttrs;
unsignedAttributeSet = info.UnsignedAttrs;
encryptionAlgorithm = info.SignatureAlgorithm;
signature = Arrays.Clone(info.Signature.GetOctets());
this.content = content;
this.calculatedDigest = calculatedDigest;
}
protected SignerInformation(SignerInformation baseInfo)
{
info = baseInfo.info;
content = baseInfo.content;
contentType = baseInfo.contentType;
isCounterSignature = baseInfo.IsCounterSignature;
sid = baseInfo.sid;
digestAlgorithm = info.DigestAlgorithm;
signedAttributeSet = info.SignedAttrs;
unsignedAttributeSet = info.UnsignedAttrs;
encryptionAlgorithm = info.SignatureAlgorithm;
signature = Arrays.Clone(info.Signature.GetOctets());
calculatedDigest = baseInfo.calculatedDigest;
signedAttributeTable = baseInfo.signedAttributeTable;
unsignedAttributeTable = baseInfo.unsignedAttributeTable;
}
public byte[] GetContentDigest()
{
if (resultDigest == null)
throw new InvalidOperationException("method can only be called after verify.");
return (byte[])resultDigest.Clone();
}
[Obsolete("Use 'SignerInfo' property instead")]
public Org.BouncyCastle.Asn1.Cms.SignerInfo ToSignerInfo()
{
return info;
}
public byte[] GetSignature()
{
return Arrays.Clone(signature);
}
public SignerInformationStore GetCounterSignatures()
{
Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttributes = UnsignedAttributes;
if (unsignedAttributes == null)
return new SignerInformationStore(new List<SignerInformation>(0));
List<SignerInformation> list = new List<SignerInformation>();
foreach (Org.BouncyCastle.Asn1.Cms.Attribute item in unsignedAttributes.GetAll(CmsAttributes.CounterSignature)) {
Asn1Set attrValues = item.AttrValues;
int count = attrValues.Count;
foreach (Org.BouncyCastle.Asn1.Cms.SignerInfo item2 in CollectionUtilities.Select(attrValues, Org.BouncyCastle.Asn1.Cms.SignerInfo.GetInstance)) {
byte[] array = DigestUtilities.CalculateDigest(item2.DigestAlgorithm.Algorithm, signature);
list.Add(new SignerInformation(item2, null, null, array));
}
}
return new SignerInformationStore(list);
}
public virtual byte[] GetEncodedSignedAttributes()
{
return signedAttributeSet?.GetEncoded("DER");
}
private bool DoVerify(AsymmetricKeyParameter publicKey)
{
AlgorithmIdentifier digestAlgorithmID = DigestAlgorithmID;
DerObjectIdentifier algorithm = digestAlgorithmID.Algorithm;
Asn1Encodable parameters = digestAlgorithmID.Parameters;
AlgorithmIdentifier signatureAlgorithm = SignatureAlgorithm;
DerObjectIdentifier algorithm2 = signatureAlgorithm.Algorithm;
Asn1Encodable parameters2 = signatureAlgorithm.Parameters;
string text;
ISigner signer;
MLDsaParameters value;
SlhDsaParameters value2;
if (EdECObjectIdentifiers.id_Ed25519.Equals(algorithm2)) {
if (parameters2 != null)
throw new CmsException("Ed25519 signature cannot specify algorithm parameters");
if (signedAttributeSet == null)
text = null;
else {
if (!NistObjectIdentifiers.IdSha512.Equals(algorithm) || parameters != null)
throw new CmsException("Ed25519 signature used with unsupported digest algorithm");
text = CmsSignedHelper.GetDigestAlgName(algorithm);
}
signer = SignerUtilities.GetSigner(algorithm2);
} else if (MLDsaParameters.ByOid.TryGetValue(algorithm2, out value)) {
if (value.IsPreHash)
throw new CmsException($"{value}""");
if (parameters2 != null)
throw new CmsException($"{value}""");
if (signedAttributeSet == null)
text = null;
else {
if (!NistObjectIdentifiers.IdSha512.Equals(algorithm) && !NistObjectIdentifiers.IdShake256.Equals(algorithm))
throw new CmsException($"{value}""");
if (parameters != null)
throw new CmsException($"{value}""");
text = CmsSignedHelper.GetDigestAlgName(algorithm);
}
signer = SignerUtilities.GetSigner(algorithm2);
} else if (SlhDsaParameters.ByOid.TryGetValue(algorithm2, out value2)) {
if (value2.IsPreHash)
throw new CmsException($"{value2}""");
if (parameters2 != null)
throw new CmsException($"{value2}""");
if (signedAttributeSet == null)
text = null;
else {
if (!CmsSignedHelper.GetSlhDsaDigestOid(algorithm2).Equals(algorithm) || parameters != null)
throw new CmsException($"{value2}""");
text = CmsSignedHelper.GetDigestAlgName(algorithm);
}
signer = SignerUtilities.GetSigner(algorithm2);
} else if (PkcsObjectIdentifiers.IdRsassaPss.Equals(algorithm2)) {
if (parameters2 == null)
throw new CmsException("RSASSA-PSS signature must specify algorithm parameters");
try {
RsassaPssParameters instance = RsassaPssParameters.GetInstance(parameters2);
if (!instance.HashAlgorithm.Algorithm.Equals(algorithm))
throw new CmsException("RSASSA-PSS signature parameters specified incorrect hash algorithm");
if (!instance.MaskGenAlgorithm.Algorithm.Equals(PkcsObjectIdentifiers.IdMgf1))
throw new CmsException("RSASSA-PSS signature parameters specified unknown MGF");
IDigest digest = DigestUtilities.GetDigest(algorithm);
int intValueExact = instance.SaltLength.IntValueExact;
if (!RsassaPssParameters.DefaultTrailerField.Equals(instance.TrailerField))
throw new CmsException("RSASSA-PSS signature parameters must have trailerField of 1");
text = CmsSignedHelper.GetDigestAlgName(algorithm);
IAsymmetricBlockCipher cipher = new RsaBlindedEngine();
signer = ((signedAttributeSet != null) ? new PssSigner(cipher, digest, intValueExact) : PssSigner.CreateRawSigner(cipher, digest, intValueExact));
} catch (Exception innerException) {
throw new CmsException("failed to set RSASSA-PSS signature parameters", innerException);
}
} else {
if (!X509Utilities.IsAbsentParameters(parameters2))
throw new CmsException("unrecognised signature parameters provided");
text = CmsSignedHelper.GetDigestAlgName(algorithm2);
if (text.Equals(algorithm2.GetID()))
text = CmsSignedHelper.GetDigestAlgName(algorithm);
signer = CmsSignedHelper.GetSignatureInstance(text + "with" + CmsSignedHelper.GetEncryptionAlgName(algorithm2));
}
try {
if (signedAttributeSet == null && text == null) {
if (content == null)
throw new CmsException("data not encapsulated in signature - use detached constructor.");
resultDigest = null;
} else if (calculatedDigest != null) {
resultDigest = calculatedDigest;
} else {
IDigest digestInstance = CmsSignedHelper.GetDigestInstance(text);
if (content != null) {
using (DigestSink outStream = new DigestSink(digestInstance))
content.Write(outStream);
} else if (signedAttributeSet == null) {
throw new CmsException("data not encapsulated in signature - use detached constructor.");
}
resultDigest = DigestUtilities.DoFinal(digestInstance);
}
} catch (IOException innerException2) {
throw new CmsException("can't process mime object to create signature.", innerException2);
}
Asn1Object singleValuedSignedAttribute = GetSingleValuedSignedAttribute(CmsAttributes.ContentType, "content-type");
if (singleValuedSignedAttribute == null) {
if (!isCounterSignature && signedAttributeSet != null)
throw new CmsException("The content-type attribute type MUST be present whenever signed attributes are present in signed-data");
} else {
if (isCounterSignature)
throw new CmsException("[For counter signatures,] the signedAttributes field MUST NOT contain a content-type attribute");
DerObjectIdentifier obj = singleValuedSignedAttribute as DerObjectIdentifier;
if (obj == null)
throw new CmsException("content-type attribute value not of ASN.1 type 'OBJECT IDENTIFIER'");
if (!obj.Equals(contentType))
throw new CmsException("content-type attribute value does not match eContentType");
}
Asn1Object singleValuedSignedAttribute2 = GetSingleValuedSignedAttribute(CmsAttributes.MessageDigest, "message-digest");
if (singleValuedSignedAttribute2 == null) {
if (signedAttributeSet != null)
throw new CmsException("the message-digest signed attribute type MUST be present when there are any signed attributes present");
} else {
Asn1OctetString asn1OctetString = singleValuedSignedAttribute2 as Asn1OctetString;
if (asn1OctetString == null)
throw new CmsException("message-digest attribute value not of ASN.1 type 'OCTET STRING'");
if (!Arrays.AreEqual(resultDigest, asn1OctetString.GetOctets()))
throw new CmsException("message-digest attribute value does not match calculated value");
}
Org.BouncyCastle.Asn1.Cms.AttributeTable signedAttributes = SignedAttributes;
if (signedAttributes == null || signedAttributes.GetAll(CmsAttributes.CounterSignature).Count <= 0) {
Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttributes = UnsignedAttributes;
if (unsignedAttributes != null) {
foreach (Org.BouncyCastle.Asn1.Cms.Attribute item in unsignedAttributes.GetAll(CmsAttributes.CounterSignature)) {
if (item.AttrValues.Count < 1)
throw new CmsException("A countersignature attribute MUST contain at least one AttributeValue");
}
}
try {
if (signedAttributeSet == null) {
if (!(signer is PssSigner)) {
if (resultDigest != null && TryGetRawVerifier(out ISigner rawVerifier))
return VerifySignature(rawVerifier, publicKey, resultDigest, signature);
signer.Init(false, publicKey);
if (content != null) {
using (SignerSink outStream2 = new SignerSink(signer))
content.Write(outStream2);
}
return signer.VerifySignature(signature);
}
return VerifySignature(signer, publicKey, resultDigest, signature);
}
return VerifySignature(signer, publicKey, GetEncodedSignedAttributes(), signature);
} catch (InvalidKeyException innerException3) {
throw new CmsException("key not appropriate to signature in message.", innerException3);
} catch (IOException innerException4) {
throw new CmsException("can't process mime object to create signature.", innerException4);
} catch (SignatureException ex) {
throw new CmsException("invalid signature format in message: " + ex.Message, ex);
}
}
throw new CmsException("A countersignature attribute MUST NOT be a signed attribute");
}
private bool TryGetRawVerifier(out ISigner rawVerifier)
{
string encryptionAlgName = CmsSignedHelper.GetEncryptionAlgName(SignatureAlgorithm.Algorithm);
if ("RSA".Equals(encryptionAlgName))
rawVerifier = new RsaDigestSigner(new NullDigest(), digestAlgorithm);
else if ("ECDSA".Equals(encryptionAlgName)) {
rawVerifier = CmsSignedHelper.GetSignatureInstance("NONEwithECDSA");
} else if ("PLAIN-ECDSA".Equals(encryptionAlgName)) {
rawVerifier = CmsSignedHelper.GetSignatureInstance("NONEwithPLAIN-ECDSA");
} else {
if (!"DSA".Equals(encryptionAlgName)) {
rawVerifier = null;
return false;
}
rawVerifier = CmsSignedHelper.GetSignatureInstance("NONEwithDSA");
}
return true;
}
public bool Verify(AsymmetricKeyParameter pubKey)
{
if (pubKey.IsPrivate)
throw new ArgumentException("Expected public key", "pubKey");
GetSigningTime();
return DoVerify(pubKey);
}
public bool Verify(X509Certificate cert)
{
Org.BouncyCastle.Asn1.Cms.Time signingTime = GetSigningTime();
if (signingTime != null)
cert.CheckValidity(signingTime.ToDateTime());
return DoVerify(cert.GetPublicKey());
}
private Asn1Object GetSingleValuedSignedAttribute(DerObjectIdentifier attrOid, string printableName)
{
Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttributes = UnsignedAttributes;
if (unsignedAttributes != null && unsignedAttributes.GetAll(attrOid).Count > 0)
throw new CmsException("The " + printableName + " attribute MUST NOT be an unsigned attribute");
Org.BouncyCastle.Asn1.Cms.AttributeTable signedAttributes = SignedAttributes;
if (signedAttributes != null) {
Asn1EncodableVector all = signedAttributes.GetAll(attrOid);
switch (all.Count) {
case 0:
return null;
case 1: {
Asn1Set attrValues = ((Org.BouncyCastle.Asn1.Cms.Attribute)all[0]).AttrValues;
if (attrValues.Count != 1)
throw new CmsException("A " + printableName + " attribute MUST have a single attribute value");
return attrValues[0].ToAsn1Object();
}
default:
throw new CmsException("The SignedAttributes in a signerInfo MUST NOT include multiple instances of the " + printableName + " attribute");
}
}
return null;
}
private Org.BouncyCastle.Asn1.Cms.Time GetSigningTime()
{
Asn1Object singleValuedSignedAttribute = GetSingleValuedSignedAttribute(CmsAttributes.SigningTime, "signing-time");
if (singleValuedSignedAttribute != null)
try {
return Org.BouncyCastle.Asn1.Cms.Time.GetInstance(singleValuedSignedAttribute);
} catch (ArgumentException) {
throw new CmsException("signing-time attribute value not a valid 'Time' structure");
}
return null;
}
public static SignerInformation ReplaceUnsignedAttributes(SignerInformation signerInformation, Org.BouncyCastle.Asn1.Cms.AttributeTable unsignedAttributes)
{
Asn1Set unauthenticatedAttributes = unsignedAttributes?.ToDerSet();
Org.BouncyCastle.Asn1.Cms.SignerInfo signerInfo = signerInformation.SignerInfo;
return new SignerInformation(new Org.BouncyCastle.Asn1.Cms.SignerInfo(signerInfo.SignerID, signerInfo.DigestAlgorithm, signerInfo.SignedAttrs, signerInfo.SignatureAlgorithm, signerInfo.Signature, unauthenticatedAttributes), signerInformation.contentType, signerInformation.content, null);
}
public static SignerInformation AddCounterSigners(SignerInformation signerInformation, SignerInformationStore counterSigners)
{
DerSet attrValues = DerSet.Map(counterSigners.SignersInternal, (SignerInformation sigInf) => sigInf.SignerInfo);
Asn1Set unauthenticatedAttributes = (signerInformation.UnsignedAttributes ?? DerSet.Empty.ToAttributeTable()).Add(new Org.BouncyCastle.Asn1.Cms.Attribute(CmsAttributes.CounterSignature, attrValues)).ToDerSet();
Org.BouncyCastle.Asn1.Cms.SignerInfo signerInfo = signerInformation.SignerInfo;
return new SignerInformation(new Org.BouncyCastle.Asn1.Cms.SignerInfo(signerInfo.SignerID, signerInfo.DigestAlgorithm, signerInfo.SignedAttrs, signerInfo.SignatureAlgorithm, signerInfo.Signature, unauthenticatedAttributes), signerInformation.contentType, signerInformation.content, null);
}
private static bool VerifySignature(ISigner verifier, ICipherParameters parameters, byte[] message, byte[] signature)
{
verifier.Init(false, parameters);
verifier.BlockUpdate(message, 0, message.Length);
return verifier.VerifySignature(signature);
}
}
}