Автор: Артём Макаров CMS подпись - это не подпись хэша данных, это подпись некоторой ASN1 структуры её содержащую.
Вы имеете в виду SignedAttributes? У меня их вроде нет.
Судя по исходникам в corefx (
https://github.com/CryptoProLLC/corefx), там подписывается именно хэш данных?
internal SignerInfoAsn Sign(
ReadOnlyMemory<byte> data,
string contentTypeOid,
bool silent,
out X509Certificate2Collection chainCerts)
{
HashAlgorithmName hashAlgorithmName = PkcsHelpers.GetDigestAlgorithm(DigestAlgorithm);
IncrementalHash hasher = IncrementalHash.CreateHash(hashAlgorithmName);
hasher.AppendData(data.Span);
byte[] dataHash = hasher.GetHashAndReset();
SignerInfoAsn newSignerInfo = new SignerInfoAsn();
newSignerInfo.DigestAlgorithm.Algorithm = DigestAlgorithm;
// If the user specified attributes (not null, count > 0) we need attributes.
// If the content type is null we're counter-signing, and need the message digest attr.
// If the content type is otherwise not-data we need to record it as the content-type attr.
if (SignedAttributes?.Count > 0 || contentTypeOid != Oids.Pkcs7Data)
{
List<AttributeAsn> signedAttrs = BuildAttributes(SignedAttributes);
using (var writer = new AsnWriter(AsnEncodingRules.DER))
{
writer.WriteOctetString(dataHash);
signedAttrs.Add(
new AttributeAsn
{
AttrType = new Oid(Oids.MessageDigest, Oids.MessageDigest),
AttrValues = new[] { new ReadOnlyMemory<byte>(writer.Encode()) },
});
}
if (contentTypeOid != null)
{
using (var writer = new AsnWriter(AsnEncodingRules.DER))
{
writer.WriteObjectIdentifier(contentTypeOid);
signedAttrs.Add(
new AttributeAsn
{
AttrType = new Oid(Oids.ContentType, Oids.ContentType),
AttrValues = new[] { new ReadOnlyMemory<byte>(writer.Encode()) },
});
}
}
// Use the serializer/deserializer to DER-normalize the attribute order.
SignedAttributesSet signedAttrsSet = new SignedAttributesSet();
signedAttrsSet.SignedAttributes = PkcsHelpers.NormalizeAttributeSet(
signedAttrs.ToArray(),
normalized => hasher.AppendData(normalized));
// Since this contains user data in a context where BER is permitted, use BER.
// There shouldn't be any observable difference here between BER and DER, though,
// since the top level fields were written by NormalizeSet.
using (AsnWriter attrsWriter = new AsnWriter(AsnEncodingRules.BER))
{
signedAttrsSet.Encode(attrsWriter);
newSignerInfo.SignedAttributes = attrsWriter.Encode();
}
dataHash = hasher.GetHashAndReset();
}
switch (SignerIdentifierType)
{
case SubjectIdentifierType.IssuerAndSerialNumber:
byte[] serial = Certificate.GetSerialNumber();
Array.Reverse(serial);
newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
{
Issuer = Certificate.IssuerName.RawData,
SerialNumber = serial,
};
newSignerInfo.Version = 1;
break;
case SubjectIdentifierType.SubjectKeyIdentifier:
newSignerInfo.Sid.SubjectKeyIdentifier = PkcsPal.Instance.GetSubjectKeyIdentifier(Certificate);
newSignerInfo.Version = 3;
break;
case SubjectIdentifierType.NoSignature:
newSignerInfo.Sid.IssuerAndSerialNumber = new IssuerAndSerialNumberAsn
{
Issuer = SubjectIdentifier.DummySignerEncodedValue,
SerialNumber = new byte[1],
};
newSignerInfo.Version = 1;
break;
default:
Debug.Fail($"Unresolved SignerIdentifierType value: {SignerIdentifierType}");
throw new CryptographicException();
}
if (UnsignedAttributes != null && UnsignedAttributes.Count > 0)
{
List<AttributeAsn> attrs = BuildAttributes(UnsignedAttributes);
newSignerInfo.UnsignedAttributes = PkcsHelpers.NormalizeAttributeSet(attrs.ToArray());
}
bool signed;
Oid signatureAlgorithm;
ReadOnlyMemory<byte> signatureValue;
if (SignerIdentifierType == SubjectIdentifierType.NoSignature)
{
signatureAlgorithm = new Oid(Oids.NoSignature, null);
signatureValue = dataHash;
signed = true;
}
else
{
signed = CmsSignature.Sign(
dataHash,
hashAlgorithmName,
Certificate,
PrivateKey,
silent,
out signatureAlgorithm,
out signatureValue);
}
if (!signed)
{
throw new CryptographicException(SR.Cryptography_Cms_CannotDetermineSignatureAlgorithm);
}
newSignerInfo.SignatureValue = signatureValue;
newSignerInfo.SignatureAlgorithm.Algorithm = signatureAlgorithm;
X509Certificate2Collection certs = new X509Certificate2Collection();
certs.AddRange(Certificates);
if (SignerIdentifierType != SubjectIdentifierType.NoSignature)
{
if (IncludeOption == X509IncludeOption.EndCertOnly)
{
certs.Add(Certificate);
}
else if (IncludeOption != X509IncludeOption.None)
{
X509Chain chain = new X509Chain();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags;
if (!chain.Build(Certificate))
{
foreach (X509ChainStatus status in chain.ChainStatus)
{
if (status.Status == X509ChainStatusFlags.PartialChain)
{
throw new CryptographicException(SR.Cryptography_Cms_IncompleteCertChain);
}
}
}
X509ChainElementCollection elements = chain.ChainElements;
int count = elements.Count;
int last = count - 1;
if (last == 0)
{
// If there's always one cert treat it as EE, not root.
last = -1;
}
for (int i = 0; i < count; i++)
{
X509Certificate2 cert = elements[i].Certificate;
if (i == last &&
IncludeOption == X509IncludeOption.ExcludeRoot &&
cert.SubjectName.RawData.AsSpan().SequenceEqual(cert.IssuerName.RawData))
{
break;
}
certs.Add(cert);
}
}
}
chainCerts = certs;
return newSignerInfo;
}