This commit is contained in:
Pranav Senthilnathan 2025-07-30 13:25:28 +02:00 committed by GitHub
commit c2f1f5d21a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1047 additions and 411 deletions

View File

@ -78,14 +78,14 @@ namespace System.Security.Cryptography.Tests
public byte[] EncryptionPasswordBytes => Encoding.UTF8.GetBytes(EncryptionPassword); // Assuming UTF-8 encoding
public byte[] Certificate => Convert.FromBase64String(CertificateBase64);
#if !EXCLUDE_PEM_ENCODING_FROM_TEST_DATA
public string EncryptedPem_Seed => PemEncoding.WriteString("ENCRYPTED PRIVATE KEY", Pkcs8EncryptedPrivateKey_Seed);
public string EncryptedPem_Expanded => PemEncoding.WriteString("ENCRYPTED PRIVATE KEY", Pkcs8EncryptedPrivateKey_Expanded);
public string EncryptedPem_Both => PemEncoding.WriteString("ENCRYPTED PRIVATE KEY", Pkcs8EncryptedPrivateKey_Both);
public string PrivateKeyPem_Seed => PemEncoding.WriteString("PRIVATE KEY", Pkcs8PrivateKey_Seed);
public string PrivateKeyPem_Expanded => PemEncoding.WriteString("PRIVATE KEY", Pkcs8PrivateKey_Expanded);
public string PrivateKeyPem_Both => PemEncoding.WriteString("PRIVATE KEY", Pkcs8PrivateKey_Both);
public string PublicKeyPem => PemEncoding.WriteString("PUBLIC KEY", Pkcs8PublicKey);
public string CertificatePem => PemEncoding.WriteString("CERTIFICATE", Certificate);
public string EncryptedPem_Seed => ByteUtils.PemEncode("ENCRYPTED PRIVATE KEY", Pkcs8EncryptedPrivateKey_Seed);
public string EncryptedPem_Expanded => ByteUtils.PemEncode("ENCRYPTED PRIVATE KEY", Pkcs8EncryptedPrivateKey_Expanded);
public string EncryptedPem_Both => ByteUtils.PemEncode("ENCRYPTED PRIVATE KEY", Pkcs8EncryptedPrivateKey_Both);
public string PrivateKeyPem_Seed => ByteUtils.PemEncode("PRIVATE KEY", Pkcs8PrivateKey_Seed);
public string PrivateKeyPem_Expanded => ByteUtils.PemEncode("PRIVATE KEY", Pkcs8PrivateKey_Expanded);
public string PrivateKeyPem_Both => ByteUtils.PemEncode("PRIVATE KEY", Pkcs8PrivateKey_Both);
public string PublicKeyPem => ByteUtils.PemEncode("PUBLIC KEY", Pkcs8PublicKey);
public string CertificatePem => ByteUtils.PemEncode("CERTIFICATE", Certificate);
#endif
public byte[] Pfx_Seed => Convert.FromBase64String(Pfx_Seed_Base64);
public byte[] Pfx_Expanded => Convert.FromBase64String(Pfx_Expanded_Base64);

View File

@ -16,8 +16,10 @@ namespace System.Security.Cryptography.Pkcs
{
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.AsymmetricAlgorithm? privateKey) { }
[System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.SlhDsa? privateKey) { }
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.MLDsa? privateKey) { }
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.RSA? privateKey, System.Security.Cryptography.RSASignaturePadding? signaturePadding) { }
[System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")]
public CmsSigner(System.Security.Cryptography.Pkcs.SubjectIdentifierType signerIdentifierType, System.Security.Cryptography.X509Certificates.X509Certificate2? certificate, System.Security.Cryptography.SlhDsa? privateKey) { }
public System.Security.Cryptography.AsymmetricAlgorithm? PrivateKey { get { throw null; } set { } }
public System.Security.Cryptography.RSASignaturePadding? SignaturePadding { get { throw null; } set { } }
}

View File

@ -84,6 +84,8 @@ namespace Internal.Cryptography.Pal.AnyOS
if (typeof(T) == typeof(DSA) && Internal.Cryptography.Helpers.IsDSASupported)
return (T?)(object?)certificate.GetDSAPrivateKey();
#endif
if (typeof(T) == typeof(MLDsa) && MLDsa.IsSupported)
return (T?)(object?)certificate.GetMLDsaPrivateKey();
if (typeof(T) == typeof(SlhDsa) && SlhDsa.IsSupported)
return (T?)(object?)certificate.GetSlhDsaPrivateKey();

View File

@ -150,6 +150,8 @@ namespace Internal.Cryptography.Pal.Windows
return (T)(object)new ECDsaCng(cngKey);
if (typeof(T) == typeof(DSA))
return (T)(object)new DSACng(cngKey);
if (typeof(T) == typeof(MLDsa))
return (T)(object)new MLDsaCng(cngKey);
if (typeof(T) == typeof(SlhDsa))
throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(SlhDsa)));

View File

@ -589,6 +589,7 @@ System.Security.Cryptography.Pkcs.EnvelopedCms</PackageDescription>
<Compile Include="System\Security\Cryptography\Pkcs\CmsSignature.cs" />
<Compile Include="System\Security\Cryptography\Pkcs\CmsSignature.ECDsa.cs" />
<Compile Include="System\Security\Cryptography\Pkcs\CmsSignature.RSA.cs" />
<Compile Include="System\Security\Cryptography\Pkcs\CmsSignature.MLDsa.cs" />
<Compile Include="System\Security\Cryptography\Pkcs\CmsSignature.SlhDsa.cs" />
<Compile Include="System\Security\Cryptography\Pkcs\CmsSigner.cs" />
<Compile Include="System\Security\Cryptography\Pkcs\SignedCms.cs" />

View File

@ -0,0 +1,118 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Security.Cryptography.X509Certificates;
namespace System.Security.Cryptography.Pkcs
{
internal partial class CmsSignature
{
static partial void PrepareRegistrationMLDsa(Dictionary<string, CmsSignature> lookup)
{
lookup.Add(Oids.MLDsa44, new MLDsaCmsSignature(Oids.MLDsa44));
lookup.Add(Oids.MLDsa65, new MLDsaCmsSignature(Oids.MLDsa65));
lookup.Add(Oids.MLDsa87, new MLDsaCmsSignature(Oids.MLDsa87));
}
private sealed class MLDsaCmsSignature : CmsSignature
{
private string _signatureAlgorithm;
internal MLDsaCmsSignature(string signatureAlgorithm)
{
_signatureAlgorithm = signatureAlgorithm;
}
protected override bool VerifyKeyType(object key) => key is MLDsa;
internal override bool NeedsHashedMessage => false;
internal override RSASignaturePadding? SignaturePadding => null;
internal override bool VerifySignature(
#if NET || NETSTANDARD2_1
ReadOnlySpan<byte> valueHash,
ReadOnlyMemory<byte> signature,
#else
byte[] valueHash,
byte[] signature,
#endif
string? digestAlgorithmOid,
ReadOnlyMemory<byte>? signatureParameters,
X509Certificate2 certificate)
{
if (signatureParameters.HasValue)
{
throw new CryptographicException(
SR.Format(SR.Cryptography_UnknownAlgorithmIdentifier, _signatureAlgorithm));
}
MLDsa? publicKey = certificate.GetMLDsaPublicKey();
if (publicKey is null)
{
return false;
}
using (publicKey)
{
return publicKey.VerifyData(
valueHash,
#if NET || NETSTANDARD2_1
signature.Span
#else
signature
#endif
);
}
}
protected override bool Sign(
#if NET || NETSTANDARD2_1
ReadOnlySpan<byte> dataHash,
#else
byte[] dataHash,
#endif
string? hashAlgorithmOid,
X509Certificate2 certificate,
object? key,
bool silent,
[NotNullWhen(true)] out string? signatureAlgorithm,
[NotNullWhen(true)] out byte[]? signatureValue,
out byte[]? signatureParameters)
{
signatureParameters = null;
signatureAlgorithm = _signatureAlgorithm;
using (GetSigningKey(key, certificate, silent, static cert => cert.GetMLDsaPublicKey(), out MLDsa? signingKey))
{
if (signingKey is null)
{
signatureValue = null;
return false;
}
// Don't pool because we will likely return this buffer to the caller.
byte[] signature = new byte[signingKey.Algorithm.SignatureSizeInBytes];
signingKey.SignData(dataHash, signature);
if (key != null)
{
using (MLDsa certKey = certificate.GetMLDsaPublicKey()!)
{
if (!certKey.VerifyData(dataHash, signature))
{
signatureValue = null;
return false;
}
}
}
signatureValue = signature;
return true;
}
}
}
}
}

View File

@ -21,12 +21,14 @@ namespace System.Security.Cryptography.Pkcs
PrepareRegistrationRsa(s_lookup);
PrepareRegistrationDsa(s_lookup);
PrepareRegistrationECDsa(s_lookup);
PrepareRegistrationMLDsa(s_lookup);
PrepareRegistrationSlhDsa(s_lookup);
}
static partial void PrepareRegistrationRsa(Dictionary<string, CmsSignature> lookup);
static partial void PrepareRegistrationDsa(Dictionary<string, CmsSignature> lookup);
static partial void PrepareRegistrationECDsa(Dictionary<string, CmsSignature> lookup);
static partial void PrepareRegistrationMLDsa(Dictionary<string, CmsSignature> lookup);
static partial void PrepareRegistrationSlhDsa(Dictionary<string, CmsSignature> lookup);
internal abstract RSASignaturePadding? SignaturePadding { get; }

View File

@ -111,6 +111,16 @@ namespace System.Security.Cryptography.Pkcs
{
}
#if NET || NETSTANDARD2_1
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
public
#else
private
#endif
CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? certificate, MLDsa? privateKey)
: this(signerIdentifierType, certificate, privateKey, signaturePadding: null)
{
}
#if NET || NETSTANDARD2_1
[Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)]
@ -193,7 +203,7 @@ namespace System.Security.Cryptography.Pkcs
Certificate = certificate;
DigestAlgorithm = s_defaultAlgorithm.CopyOid();
Debug.Assert(privateKey is null or AsymmetricAlgorithm or SlhDsa);
Debug.Assert(privateKey is null or AsymmetricAlgorithm or MLDsa or SlhDsa);
_privateKey = (IDisposable?)privateKey;
_signaturePadding = signaturePadding;
@ -371,36 +381,8 @@ namespace System.Security.Cryptography.Pkcs
if (SignerIdentifierType == SubjectIdentifierType.NoSignature)
{
// The behavior of this scenario should match Windows which currently does not
// implement PQC. So we do a best effort determination of whether the algorithm
// is a pure algorithm and throw if so. This is subject to change once Windows
// implements PQC.
string? keyAlgorithm = null;
if (Certificate != null)
{
try
{
keyAlgorithm = Certificate.GetKeyAlgorithm();
}
catch (CryptographicException)
{
}
}
if (keyAlgorithm != null)
{
CmsSignature? processor = CmsSignature.ResolveAndVerifyKeyType(keyAlgorithm, _privateKey, SignaturePadding);
if (processor?.NeedsHashedMessage == false)
{
throw new CryptographicException(SR.Cryptography_Cms_CertificateDoesNotSupportNoSignature);
}
}
ReadOnlyMemory<byte> messageToSign =
GetMessageToSign(shouldHash: true, data, contentTypeOid, out newSignerInfo.SignedAttributes);
signatureAlgorithm = Oids.NoSignature;
signatureValue = messageToSign;
signatureValue = GetMessageToSign(shouldHash: true, data, contentTypeOid, out newSignerInfo.SignedAttributes);
signed = true;
}
else

View File

@ -1,8 +1,10 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.SLHDsa.Tests;
using System.Security.Cryptography.Tests;
using Test.Cryptography;
namespace System.Security.Cryptography.Pkcs.Tests
@ -35,12 +37,14 @@ namespace System.Security.Cryptography.Pkcs.Tests
public static readonly CertLoader RsaOaep2048_Sha256Parameters = new CertLoaderFromRawData(RawData.RsaOaep2048_Sha256ParametersCert, RawData.RsaOaep2048_Sha256ParametersPfx, "1111");
public static readonly CertLoader RsaOaep2048_NoParameters = new CertLoaderFromRawData(RawData.RsaOaep2048_NoParametersCert, RawData.RsaOaep2048_NoParametersPfx, "1111");
public static readonly CertLoader SlhDsaSha2_128s_Ietf = new CertLoaderFromRawData(SlhDsaTestData.IetfSlhDsaSha2_128sCertificate, SlhDsaTestData.IetfSlhDsaSha2_128sCertificatePfx, "PLACEHOLDER");
public static readonly CertLoader[] SlhDsaGeneratedCerts = LoadSlhDsaCerts();
public static readonly CertLoader[] SlhDsaGeneratedCerts = [..SlhDsaTestData.GeneratedKeyInfosRaw.Select(info => new CertLoaderFromRawData(info.Certificate, info.SelfSignedCertificatePfx, info.EncryptionPassword))];
private static CertLoader[] LoadSlhDsaCerts() =>
SlhDsaTestData.GeneratedKeyInfosRaw
.Select(info => new CertLoaderFromRawData(info.Certificate, info.SelfSignedCertificatePfx, info.EncryptionPassword))
.ToArray();
public static readonly Dictionary<MLDsaAlgorithm, CertLoader> MLDsaIetf = new()
{
{ MLDsaAlgorithm.MLDsa44, new CertLoaderFromRawData(MLDsaTestsData.IetfMLDsa44.Certificate, MLDsaTestsData.IetfMLDsa44.Pfx_Seed, "PLACEHOLDER") },
{ MLDsaAlgorithm.MLDsa65, new CertLoaderFromRawData(MLDsaTestsData.IetfMLDsa65.Certificate, MLDsaTestsData.IetfMLDsa65.Pfx_Seed, "PLACEHOLDER") },
{ MLDsaAlgorithm.MLDsa87, new CertLoaderFromRawData(MLDsaTestsData.IetfMLDsa87.Certificate, MLDsaTestsData.IetfMLDsa87.Pfx_Seed, "PLACEHOLDER") },
};
// Note: the raw data is its own (nested) class to avoid problems with static field initialization ordering.
private static class RawData

View File

@ -1,11 +1,11 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Security.Cryptography.SLHDsa.Tests;
using System.Security.Cryptography.Tests;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography.Xml;
using Test.Cryptography;
@ -15,6 +15,9 @@ namespace System.Security.Cryptography.Pkcs.Tests
{
public static partial class SignedCmsTests
{
// TODO: Windows does not support draft 10 PKCS#8 format yet. Remove this and use MLDsa.IsSupported when it does.
public static bool SupportsDraft10Pkcs8 => MLDsa.IsSupported && !PlatformDetection.IsWindows;
[Fact]
public static void DefaultStateBehavior()
{
@ -461,47 +464,19 @@ namespace System.Security.Cryptography.Pkcs.Tests
[InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true)]
public static void AddFirstSigner_RSA(SubjectIdentifierType identifierType, bool detached)
{
ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
SignedCms cms = new SignedCms(contentInfo, detached);
using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
cms.ComputeSignature(signer);
}
Assert.Same(contentInfo.Content, cms.ContentInfo.Content);
Assert.Single(cms.SignerInfos);
Assert.Single(cms.Certificates);
int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1;
Assert.Equal(expectedVersion, cms.Version);
SignerInfo firstSigner = cms.SignerInfos[0];
Assert.Equal(identifierType, firstSigner.SignerIdentifier.Type);
Assert.NotNull(firstSigner.Certificate);
Assert.NotSame(cms.Certificates[0], firstSigner.Certificate);
Assert.Equal(cms.Certificates[0], firstSigner.Certificate);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms = new SignedCms();
cms.Decode(encoded);
Assert.Single(cms.SignerInfos);
Assert.Single(cms.Certificates);
Assert.Equal(expectedVersion, cms.Version);
Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type);
Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate);
if (detached)
{
Assert.Throws<CryptographicException>(() => cms.CheckSignature(true));
cms = new SignedCms(contentInfo, detached);
cms.Decode(encoded);
}
cms.CheckSignature(true);
AssertAddFirstSigner(
identifierType,
detached,
cms =>
{
using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
cms.ComputeSignature(signer);
}
},
firstSigner => { /* No additional asserts */ },
roundtrippedFirstSigner => { /* No additional asserts */ });
}
[Fact]
@ -542,62 +517,41 @@ namespace System.Security.Cryptography.Pkcs.Tests
[SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")]
public static void AddFirstSigner_DSA(SubjectIdentifierType identifierType, bool detached)
{
ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
SignedCms cms = new SignedCms(contentInfo, detached);
using (X509Certificate2 signerCert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
// Best compatibility for DSA is SHA-1 (FIPS 186-2)
signer.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1);
cms.ComputeSignature(signer);
}
Assert.Single(cms.SignerInfos);
Assert.Single(cms.Certificates);
int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1;
Assert.Equal(expectedVersion, cms.Version);
SignerInfo firstSigner = cms.SignerInfos[0];
Assert.Equal(identifierType, firstSigner.SignerIdentifier.Type);
Assert.NotNull(firstSigner.Certificate);
Assert.NotSame(cms.Certificates[0], firstSigner.Certificate);
Assert.Equal(cms.Certificates[0], firstSigner.Certificate);
#if NET
byte[] signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// DSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
byte[]? signature = null;
#endif
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms = new SignedCms();
cms.Decode(encoded);
Assert.Single(cms.SignerInfos);
Assert.Single(cms.Certificates);
Assert.Equal(expectedVersion, cms.Version);
Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type);
Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate);
AssertAddFirstSigner(
identifierType,
detached,
cms =>
{
using (X509Certificate2 signerCert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
// Best compatibility for DSA is SHA-1 (FIPS 186-2)
signer.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1);
cms.ComputeSignature(signer);
}
},
firstSigner =>
{
#if NET
byte[] sig2 = cms.SignerInfos[0].GetSignature();
Assert.Equal(signature, sig2);
// Store signature for comparison after roundtrip.
signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// DSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
#endif
if (detached)
{
Assert.Throws<CryptographicException>(() => cms.CheckSignature(true));
cms = new SignedCms(contentInfo, detached);
cms.Decode(encoded);
}
cms.CheckSignature(true);
},
roundtrippedFirstSigner =>
{
#if NET
byte[] sig2 = roundtrippedFirstSigner.GetSignature();
Assert.Equal(signature, sig2);
#endif
});
}
[Theory]
@ -614,64 +568,43 @@ namespace System.Security.Cryptography.Pkcs.Tests
[InlineData(SubjectIdentifierType.SubjectKeyIdentifier, true, Oids.Sha512)]
public static void AddFirstSigner_ECDSA(SubjectIdentifierType identifierType, bool detached, string digestOid)
{
ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
SignedCms cms = new SignedCms(contentInfo, detached);
using (X509Certificate2 signerCert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
cms.ComputeSignature(signer);
}
Assert.Single(cms.SignerInfos);
Assert.Single(cms.Certificates);
int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1;
Assert.Equal(expectedVersion, cms.Version);
SignerInfo firstSigner = cms.SignerInfos[0];
Assert.Equal(identifierType, firstSigner.SignerIdentifier.Type);
Assert.NotNull(firstSigner.Certificate);
Assert.NotSame(cms.Certificates[0], firstSigner.Certificate);
Assert.Equal(cms.Certificates[0], firstSigner.Certificate);
#if NET
byte[] signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// ECDSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
// ECDSA Oids are all under 1.2.840.10045.4.
Assert.StartsWith("1.2.840.10045.4.", firstSigner.SignatureAlgorithm.Value);
byte[]? signature = null;
#endif
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms = new SignedCms();
cms.Decode(encoded);
Assert.Single(cms.SignerInfos);
Assert.Single(cms.Certificates);
Assert.Equal(expectedVersion, cms.Version);
Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type);
Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate);
AssertAddFirstSigner(
identifierType,
detached,
cms =>
{
using (X509Certificate2 signerCert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
cms.ComputeSignature(signer);
}
},
firstSigner =>
{
#if NET
byte[] sig2 = cms.SignerInfos[0].GetSignature();
Assert.Equal(signature, sig2);
// Store signature for comparison after roundtrip.
signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// ECDSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
// ECDSA Oids are all under 1.2.840.10045.4.
Assert.StartsWith("1.2.840.10045.4.", firstSigner.SignatureAlgorithm.Value);
#endif
if (detached)
{
Assert.Throws<CryptographicException>(() => cms.CheckSignature(true));
cms = new SignedCms(contentInfo, detached);
cms.Decode(encoded);
}
cms.CheckSignature(true);
},
roundtrippedFirstSigner =>
{
#if NET
byte[] sig2 = roundtrippedFirstSigner.GetSignature();
Assert.Equal(signature, sig2);
#endif
});
}
public static IEnumerable<object[]> AddFirstSignerSlhDsaTestData =>
@ -691,20 +624,103 @@ namespace System.Security.Cryptography.Pkcs.Tests
[ConditionalTheory(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
[MemberData(nameof(AddFirstSignerSlhDsaTestData))]
public static void AddFirstSigner_SlhDsa(SubjectIdentifierType identifierType, bool detached, string digestOid, SlhDsaTestData.SlhDsaGeneratedKeyInfo info)
{
byte[]? signature = null;
AssertAddFirstSigner(
identifierType,
detached,
cms =>
{
CertLoader loader = Certificates.SlhDsaGeneratedCerts.Single(cert => cert.CerData.SequenceEqual(info.Certificate));
using (X509Certificate2 signerCert = loader.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
cms.ComputeSignature(signer);
}
},
firstSigner =>
{
// Store signature for comparison after roundtrip.
signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// SLH-DSA Oids are all under 2.16.840.1.101.3.4.3.
Assert.StartsWith("2.16.840.1.101.3.4.3.", firstSigner.SignatureAlgorithm.Value);
},
roundtrippedFirstSigner =>
{
byte[] sig2 = roundtrippedFirstSigner.GetSignature();
Assert.Equal(signature, sig2);
});
}
public static IEnumerable<object[]> AddFirstSignerMLDsaTestData =>
from sit in new[] { SubjectIdentifierType.IssuerAndSerialNumber, SubjectIdentifierType.SubjectKeyIdentifier }
from detached in new[] { false, true }
from data in new (MLDsaAlgorithm algorithm, string hashAlgorithm)[]
{
(MLDsaAlgorithm.MLDsa44, Oids.Shake128),
(MLDsaAlgorithm.MLDsa65, Oids.Sha512),
(MLDsaAlgorithm.MLDsa87, Oids.Shake256),
}
select new object[] { sit, detached, data.hashAlgorithm, data.algorithm };
[ConditionalTheory(nameof(SupportsDraft10Pkcs8))]
[MemberData(nameof(AddFirstSignerMLDsaTestData))]
public static void AddFirstSigner_MLDsa(SubjectIdentifierType identifierType, bool detached, string digestOid, MLDsaAlgorithm algorithm)
{
byte[]? signature = null;
AssertAddFirstSigner(
identifierType,
detached,
cms =>
{
using (X509Certificate2 signerCert = Certificates.MLDsaIetf[algorithm].TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
cms.ComputeSignature(signer);
}
},
firstSigner =>
{
// Store signature for comparison after roundtrip.
signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// ML-DSA Oids are all under 2.16.840.1.101.3.4.3.
Assert.StartsWith("2.16.840.1.101.3.4.3.", firstSigner.SignatureAlgorithm.Value);
},
roundtrippedFirstSigner =>
{
byte[] sig2 = roundtrippedFirstSigner.GetSignature();
Assert.Equal(signature, sig2);
});
}
private static void AssertAddFirstSigner(
SubjectIdentifierType identifierType,
bool detached,
Action<SignedCms> signCms,
Action<SignerInfo> assertFirstSigner,
Action<SignerInfo> assertRoundtrippedFirstSigner)
{
ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
SignedCms cms = new SignedCms(contentInfo, detached);
CertLoader loader = Certificates.SlhDsaGeneratedCerts.Single(cert => cert.CerData.SequenceEqual(info.Certificate));
using (X509Certificate2 signerCert = loader.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
cms.ComputeSignature(signer);
}
signCms(cms);
Assert.Same(contentInfo.Content, cms.ContentInfo.Content);
Assert.Single(cms.SignerInfos);
// Currently the test assumes only a single certificate is added.
// If this assertion fails in newly added tests, update the provided signing
// callback to use EndCertOnly.
Assert.Single(cms.Certificates);
int expectedVersion = identifierType == SubjectIdentifierType.SubjectKeyIdentifier ? 3 : 1;
@ -716,11 +732,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
Assert.NotSame(cms.Certificates[0], firstSigner.Certificate);
Assert.Equal(cms.Certificates[0], firstSigner.Certificate);
byte[] signature = firstSigner.GetSignature();
Assert.NotEmpty(signature);
// SLH-DSA Oids are all under 2.16.840.1.101.3.4.3.
Assert.StartsWith("2.16.840.1.101.3.4.3.", firstSigner.SignatureAlgorithm.Value);
assertFirstSigner(firstSigner);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
@ -734,8 +746,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
Assert.Equal(identifierType, cms.SignerInfos[0].SignerIdentifier.Type);
Assert.Equal(firstSigner.Certificate, cms.SignerInfos[0].Certificate);
byte[] sig2 = cms.SignerInfos[0].GetSignature();
Assert.Equal(signature, sig2);
assertRoundtrippedFirstSigner(cms.SignerInfos[0]);
if (detached)
{
@ -1688,21 +1699,6 @@ namespace System.Security.Cryptography.Pkcs.Tests
cms.CheckSignature(verifySignatureOnly: true);
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void ComputeSignature_SlhDsa_NoSignature()
{
ContentInfo contentInfo = new ContentInfo(new byte[] { 9, 8, 7, 6, 5 });
SignedCms cms = new SignedCms(contentInfo, false);
using (X509Certificate2 cert = Certificates.SlhDsaSha2_128s_Ietf.GetCertificate())
{
CmsSigner cmsSigner = new CmsSigner(SubjectIdentifierType.NoSignature, cert);
AssertExtensions.ThrowsContains<CryptographicException>(
() => cms.ComputeSignature(cmsSigner),
"SignatureIdentifierType.NoSignature is not valid with the provided certificate.");
}
}
// Ed25519 certificate from https://datatracker.ietf.org/doc/html/rfc8410#section-10.2
private const string UnknownAlgorithmCert =
"""
@ -1748,11 +1744,26 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
[Fact]
public static void ComputeSignature_NoSignature_DefaultDigest()
public static void ComputeSignature_Rsa_NoSignature_DefaultDigest()
{
// A certificate shouldn't really be required here, but on .NET Framework
// it will encounter throw a NullReferenceException.
using X509Certificate2 cert = Certificates.RSAKeyTransferCapi1.GetCertificate();
ComputeSignature_NoSignature_DefaultDigest(Certificates.RSAKeyTransferCapi1.GetCertificate);
}
[ConditionalFact(typeof(MLDsa), nameof(MLDsa.IsSupported))]
public static void ComputeSignature_MLDsa_NoSignature_DefaultDigest()
{
ComputeSignature_NoSignature_DefaultDigest(Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].GetCertificate);
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void ComputeSignature_SlhDsa_NoSignature_DefaultDigest()
{
ComputeSignature_NoSignature_DefaultDigest(Certificates.SlhDsaSha2_128s_Ietf.GetCertificate);
}
private static void ComputeSignature_NoSignature_DefaultDigest(Func<X509Certificate2> getCert)
{
using X509Certificate2 cert = getCert();
byte[] message = "Hello World!"u8.ToArray();
SignedCms cms = new SignedCms(new ContentInfo(message));
@ -1762,8 +1773,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
cms.ComputeSignature(signer);
FrameworkName fwkName = new FrameworkName(AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName);
bool defaultHashIsSha1 = PlatformDetection.IsNetFramework && fwkName.Version <= new Version(4, 7, 0);
bool defaultHashIsSha1 = IsNetFramework471OrLower;
byte[] expectedMessageHash = Convert.FromBase64String(
defaultHashIsSha1
? "Lve95gjOVATpfV8EL5X4nxwjKHE=" // Sha1
@ -1791,5 +1801,105 @@ namespace System.Security.Cryptography.Pkcs.Tests
cms.CheckHash();
}
}
[ConditionalFact(nameof(SupportsDraft10Pkcs8))]
public static void ComputeSignature_MLDsa_DefaultDigest()
{
#if !NETFRAMEWORK
// Test signer with public certificate and private key
AssertSignerHasCorrectDefaultDigest(
useSigner =>
{
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].GetCertificate())
using (MLDsa key = MLDsa.ImportMLDsaPrivateSeed(MLDsaAlgorithm.MLDsa65, MLDsaTestsData.IetfMLDsa65.PrivateSeed))
{
useSigner(new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, key));
}
});
// Test signer with private certificate
AssertSignerHasCorrectDefaultDigest(
useSigner =>
{
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
{
useSigner(new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, (MLDsa?)null));
}
});
#endif
// Test signer with private certificate
AssertSignerHasCorrectDefaultDigest(
useSigner =>
{
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
{
useSigner(new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert));
}
});
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void ComputeSignature_SlhDsa_DefaultDigest()
{
#if !NETFRAMEWORK
// Test signer with public certificate and private key
AssertSignerHasCorrectDefaultDigest(
useSigner =>
{
using (X509Certificate2 cert = Certificates.SlhDsaSha2_128s_Ietf.GetCertificate())
using (SlhDsa key = SlhDsa.ImportSlhDsaSecretKey(SlhDsaAlgorithm.SlhDsaSha2_128s, SlhDsaTestData.IetfSlhDsaSha2_128sPrivateKeyValue))
{
useSigner(new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, key));
}
});
// Test signer with private certificate
AssertSignerHasCorrectDefaultDigest(
useSigner =>
{
using (X509Certificate2 cert = Certificates.SlhDsaSha2_128s_Ietf.TryGetCertificateWithPrivateKey())
{
useSigner(new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, (SlhDsa?)null));
}
});
#endif
// Test signer with private certificate
AssertSignerHasCorrectDefaultDigest(
useSigner =>
{
using (X509Certificate2 cert = Certificates.SlhDsaSha2_128s_Ietf.TryGetCertificateWithPrivateKey())
{
useSigner(new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert));
}
});
}
private static void AssertSignerHasCorrectDefaultDigest(Action<Action<CmsSigner>> test)
{
// DigestAlgorithm property on new signer has correct default value
test(static signer => Assert.Equal(DefaultHashForPlatform.Value, signer.DigestAlgorithm.Value));
// Signer signs with correct digest value
test(
static signer =>
{
byte[] message = "Hello World!"u8.ToArray();
SignedCms cms = new SignedCms(new ContentInfo(message));
cms.ComputeSignature(signer);
Assert.Equal(DefaultHashForPlatform.Value, cms.SignerInfos[0].DigestAlgorithm.Value);
// Assert.NoThrow
cms.SignerInfos[0].CheckSignature(verifySignatureOnly: true);
});
}
private static bool IsNetFramework471OrLower { get; } =
PlatformDetection.IsNetFramework && new FrameworkName(AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName).Version <= new Version(4, 7, 1);
private static Oid DefaultHashForPlatform = IsNetFramework471OrLower ? new Oid(Oids.Sha1, Oids.Sha1) : new Oid(Oids.Sha256, Oids.Sha256);
}
}

View File

@ -5,9 +5,8 @@ using System.Collections.Generic;
using System.Formats.Asn1;
using System.Linq;
using System.Security.Cryptography.SLHDsa.Tests;
using System.Security.Cryptography.Tests;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Text.Unicode;
using Test.Cryptography;
using Xunit;
@ -104,6 +103,16 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalFact(nameof(SupportsDraft10Pkcs8))]
public static void SignCmsUsingExplicitMLDsaKey()
{
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
using (MLDsa key = cert.GetMLDsaPrivateKey())
{
VerifyWithExplicitPrivateKey(cert, key);
}
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void SignCmsUsingExplicitSlhDsaKey()
{
@ -152,6 +161,30 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalFact(nameof(SupportsDraft10Pkcs8))]
public static void CounterSignCmsUsingExplicitECDsaKeyForFirstSignerAndMLDsaForCounterSignature()
{
using (X509Certificate2 cert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey())
using (ECDsa key = cert.GetECDsaPrivateKey())
using (X509Certificate2 counterSignerCert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
using (MLDsa counterSignerKey = counterSignerCert.GetMLDsaPrivateKey())
{
VerifyCounterSignatureWithExplicitPrivateKey(cert, key, counterSignerCert, counterSignerKey);
}
}
[ConditionalFact(nameof(SupportsDraft10Pkcs8))]
public static void CounterSignCmsUsingExplicitMLDsaKeyForFirstSignerAndRSAForCounterSignature()
{
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
using (MLDsa key = cert.GetMLDsaPrivateKey())
using (X509Certificate2 counterSignerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
using (RSA counterSignerKey = counterSignerCert.GetRSAPrivateKey())
{
VerifyCounterSignatureWithExplicitPrivateKey(cert, key, counterSignerCert, counterSignerKey);
}
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void CounterSignCmsUsingExplicitECDsaKeyForFirstSignerAndSlhDsaForCounterSignature()
{
@ -226,6 +259,38 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalFact(typeof(MLDsa), nameof(MLDsa.IsSupported))]
public static void SignCmsUsingECDsaCertAndMLDsaKeyThrows()
{
byte[] content = { 9, 8, 7, 6, 5 };
ContentInfo contentInfo = new ContentInfo(content);
SignedCms cms = new SignedCms(contentInfo, detached: false);
using (X509Certificate2 cert = Certificates.ECDsaP256Win.GetCertificate())
using (MLDsa key = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65))
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, key);
Assert.Throws<CryptographicException>(() => cms.ComputeSignature(signer));
}
}
[ConditionalFact(typeof(MLDsa), nameof(MLDsa.IsSupported))]
public static void SignCmsUsingMLDsaCertAndRSAKeyThrows()
{
byte[] content = { 9, 8, 7, 6, 5 };
ContentInfo contentInfo = new ContentInfo(content);
SignedCms cms = new SignedCms(contentInfo, detached: false);
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].GetCertificate())
using (RSA key = RSA.Create())
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, key);
Assert.Throws<CryptographicException>(() => cms.ComputeSignature(signer));
}
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void SignCmsUsingECDsaCertAndSlhDsaKeyThrows()
{
@ -309,6 +374,22 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalFact(typeof(MLDsa), nameof(MLDsa.IsSupported))]
public static void SignCmsUsingMLDsaCertWithNotMatchingKeyThrows()
{
byte[] content = { 9, 8, 7, 6, 5 };
ContentInfo contentInfo = new ContentInfo(content);
SignedCms cms = new SignedCms(contentInfo, detached: false);
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].GetCertificate())
using (MLDsa key = MLDsa.GenerateKey(MLDsaAlgorithm.MLDsa65))
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.SubjectKeyIdentifier, cert, key);
Assert.Throws<CryptographicException>(() => cms.ComputeSignature(signer));
}
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void SignCmsUsingSlhDsaCertWithNotMatchingKeyThrows()
{
@ -556,23 +637,37 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalFact(typeof(MLDsa), nameof(MLDsa.IsSupported))]
public static void AddSigner_MLDsa_EphemeralKey()
{
using (MLDsa MLDsa = MLDsa.ImportMLDsaPrivateSeed(MLDsaAlgorithm.MLDsa65, MLDsaTestsData.IetfMLDsa65.PrivateSeed))
using (X509Certificate2 publicCertificate = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].GetCertificate())
using (X509Certificate2 certWithEphemeralKey = publicCertificate.CopyWithPrivateKey(MLDsa))
{
ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
SignedCms cms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(certWithEphemeralKey)
{
IncludeOption = X509IncludeOption.EndCertOnly
};
cms.ComputeSignature(signer);
}
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void AddSigner_SlhDsa_EphemeralKey()
{
using (SlhDsa slhDsa = SlhDsa.ImportSlhDsaSecretKey(SlhDsaAlgorithm.SlhDsaSha2_128s, SlhDsaTestData.IetfSlhDsaSha2_128sPrivateKeyValue))
using (X509Certificate2 publicCertificate = Certificates.SlhDsaSha2_128s_Ietf.GetCertificate())
using (X509Certificate2 certificateWithKey = Certificates.SlhDsaSha2_128s_Ietf.TryGetCertificateWithPrivateKey(exportable: true))
using (X509Certificate2 certWithEphemeralKey = publicCertificate.CopyWithPrivateKey(slhDsa))
{
using (X509Certificate2 certWithEphemeralKey = publicCertificate.CopyWithPrivateKey(slhDsa))
ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
SignedCms cms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(certWithEphemeralKey)
{
ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
SignedCms cms = new SignedCms(content, false);
CmsSigner signer = new CmsSigner(certWithEphemeralKey)
{
IncludeOption = X509IncludeOption.EndCertOnly
};
cms.ComputeSignature(signer);
}
IncludeOption = X509IncludeOption.EndCertOnly
};
cms.ComputeSignature(signer);
}
}
@ -717,6 +812,40 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalFact(nameof(SupportsDraft10Pkcs8))]
public static void CreateSignature_MLDsa_ThrowsWithRsaSignaturePadding()
{
ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
SignedCms cms = new SignedCms(content);
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert, null, RSASignaturePadding.Pss);
Assert.ThrowsAny<CryptographicException>(() => cms.ComputeSignature(signer));
}
}
[ConditionalTheory(typeof(MLDsa), nameof(MLDsa.IsSupported))]
[InlineData(Oids.RsaPkcs1Sha256)]
public static void ComputeSignature_MLDsa_ThrowsWithUnsupportedHash(string hashAlgorithm)
{
ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
SignedCms cms = new SignedCms(content);
MLDsa MLDsa =
MLDsa.ImportMLDsaPrivateSeed(
MLDsaAlgorithm.MLDsa65,
MLDsaTestsData.IetfMLDsa65.PrivateSeed);
using (MLDsa)
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].GetCertificate())
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert);
signer.DigestAlgorithm = new Oid(hashAlgorithm, null);
Assert.Throws<CryptographicException>(() => cms.ComputeSignature(signer));
}
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void CreateSignature_SlhDsa_ThrowsWithRsaSignaturePadding()
{
@ -891,6 +1020,33 @@ namespace System.Security.Cryptography.Pkcs.Tests
}
}
[ConditionalTheory(nameof(SupportsDraft10Pkcs8))]
[InlineData(Oids.Sha3_256)]
[InlineData(Oids.Sha3_384)]
[InlineData(Oids.Sha3_512)]
public static void ComputeSignature_MLDsa_Roundtrip(string hashAlgorithm)
{
ContentInfo content = new ContentInfo(new byte[] { 1, 2, 3 });
SignedCms cms = new SignedCms(content);
byte[] cmsBytes;
using (X509Certificate2 cert = Certificates.MLDsaIetf[MLDsaAlgorithm.MLDsa65].TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, cert);
signer.DigestAlgorithm = new Oid(hashAlgorithm, null);
cms.ComputeSignature(signer);
cmsBytes = cms.Encode();
cms = new SignedCms();
cms.Decode(cmsBytes);
cms.CheckSignature(true); // Assert.NoThrow
Assert.Single(cms.SignerInfos);
SignerInfo signerInfo = cms.SignerInfos[0];
Assert.Equal(hashAlgorithm, signerInfo.DigestAlgorithm.Value);
}
}
[ConditionalTheory(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
[InlineData(Oids.Sha3_256)]
[InlineData(Oids.Sha3_384)]
@ -1010,17 +1166,6 @@ namespace System.Security.Cryptography.Pkcs.Tests
Assert.Equal(Oids.EcPublicKey, signerInfo.SignatureAlgorithm.Value);
}
private delegate CmsSigner CreateSignerFunc<TKey>(SubjectIdentifierType sit, X509Certificate2 cert, TKey key);
private static CreateSignerFunc<AsymmetricAlgorithm> CreateAsymmetricAlgorithmSigner = (sit, cert, key) =>
{
return new CmsSigner(sit, cert, key);
};
private static CreateSignerFunc<SlhDsa> CreateSlhDsaSigner = (sit, cert, key) =>
{
return new CmsSigner(sit, cert, key);
};
private static void VerifyWithExplicitPrivateKey(X509Certificate2 cert, object key)
{
using (var pubCert = new X509Certificate2(cert.RawData))
@ -1082,6 +1227,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
{
AsymmetricAlgorithm asymmetricKey => new CmsSigner(sit, cert, asymmetricKey),
SlhDsa slhDsaKey => new CmsSigner(sit, cert, slhDsaKey),
MLDsa mldsaKey => new CmsSigner(sit, cert, mldsaKey),
_ => throw new NotSupportedException($"Unsupported key type: {key.GetType().Name}"),
};
}

View File

@ -617,6 +617,116 @@ namespace System.Security.Cryptography.Pkcs.Tests
cms.CheckHash();
}
[ConditionalFact(typeof(MLDsa), nameof(MLDsa.IsSupported))]
public static void ReadMLDsaDocument()
{
SignedCms cms = new SignedCms();
cms.Decode(SignedDocuments.MLDsa65_Sha256_SignedDocument);
Assert.Equal(1, cms.Version);
ContentInfo contentInfo = cms.ContentInfo;
Assert.Equal("1.2.840.113549.1.7.1", contentInfo.ContentType.Value);
AssertExtensions.SequenceEqual("Hello World!"u8, contentInfo.Content);
X509Certificate2Collection certs = cms.Certificates;
Assert.Single(certs);
X509Certificate2 topLevelCert = certs[0];
Assert.Equal("LAMPS WG", topLevelCert.GetNameInfo(X509NameType.SimpleName, false));
Assert.Equal(
new DateTimeOffset(2020, 2, 3, 4, 32, 10, TimeSpan.Zero),
new DateTimeOffset(topLevelCert.NotBefore));
Assert.Equal(
new DateTimeOffset(2040, 1, 29, 4, 32, 10, TimeSpan.Zero),
new DateTimeOffset(topLevelCert.NotAfter));
SignerInfoCollection signers = cms.SignerInfos;
Assert.Single(signers);
SignerInfo signer = signers[0];
Assert.Equal(1, signer.Version);
Assert.Equal(SubjectIdentifierType.IssuerAndSerialNumber, signer.SignerIdentifier.Type);
X509IssuerSerial issuerSerial = (X509IssuerSerial)signer.SignerIdentifier.Value;
Assert.Equal(topLevelCert.IssuerName.Name, issuerSerial.IssuerName);
Assert.Equal("159FFE6F22FD5CC42C524DF6FD5E28D0DE38F34E", issuerSerial.SerialNumber);
Assert.Equal("2.16.840.1.101.3.4.2.1", signer.DigestAlgorithm.Value);
#if NET
Assert.Equal("2.16.840.1.101.3.4.3.18", signer.SignatureAlgorithm.Value);
#endif
CryptographicAttributeObjectCollection signedAttrs = signer.SignedAttributes;
Assert.Equal(4, signedAttrs.Count);
Assert.Equal("1.2.840.113549.1.9.3", signedAttrs[0].Oid.Value);
Assert.Equal("1.2.840.113549.1.9.5", signedAttrs[1].Oid.Value);
Assert.Equal("1.2.840.113549.1.9.4", signedAttrs[2].Oid.Value);
Assert.Equal("1.2.840.113549.1.9.15", signedAttrs[3].Oid.Value);
Assert.Equal(1, signedAttrs[0].Values.Count);
Assert.Equal(1, signedAttrs[1].Values.Count);
Assert.Equal(1, signedAttrs[2].Values.Count);
Assert.Equal(1, signedAttrs[3].Values.Count);
Pkcs9ContentType contentTypeAttr = (Pkcs9ContentType)signedAttrs[0].Values[0];
Assert.Equal("1.2.840.113549.1.7.1", contentTypeAttr.ContentType.Value);
Pkcs9SigningTime signingTimeAttr = (Pkcs9SigningTime)signedAttrs[1].Values[0];
Assert.Equal(
new DateTimeOffset(2025, 7, 26, 21, 52, 13, TimeSpan.Zero),
new DateTimeOffset(signingTimeAttr.SigningTime));
Pkcs9MessageDigest messageDigestAttr = (Pkcs9MessageDigest)signedAttrs[2].Values[0];
Assert.Equal(
"7F83B1657FF1FC53B92DC18148A1D65DFC2D4B1FA3D677284ADDD200126D9069",
messageDigestAttr.MessageDigest.ByteArrayToHex());
Assert.IsType<Pkcs9AttributeObject>(signedAttrs[3].Values[0]);
#if !NET
Assert.NotSame(signedAttrs[3].Oid, signedAttrs[3].Values[0].Oid);
#endif
Assert.Equal(
"306A300B060960864801650304012A300B0609608648016503040116300B0609" +
"608648016503040102300A06082A864886F70D0307300E06082A864886F70D03" +
"0202020080300D06082A864886F70D0302020140300706052B0E030207300D06" +
"082A864886F70D0302020128",
signedAttrs[3].Values[0].RawData.ByteArrayToHex());
#if NET
// Long signature so just check the end
AssertExtensions.TrueExpression(
signer.GetSignature().ByteArrayToHex().EndsWith("0A134051558694BB000000000000000000000C131A20252D"));
#endif
CryptographicAttributeObjectCollection unsignedAttrs = signer.UnsignedAttributes;
Assert.Empty(unsignedAttrs);
SignerInfoCollection counterSigners = signer.CounterSignerInfos;
Assert.Empty(counterSigners);
X509Certificate2 signerCertificate = signer.Certificate;
Assert.Equal(
"CN=LAMPS WG, O=IETF",
signerCertificate.SubjectName.Name);
// CheckHash always throws for certificate-based signers.
Assert.Throws<CryptographicException>(() => signer.CheckHash());
// Assert.NoThrows
signer.CheckSignature(true);
// Since there are no NoSignature signers the document CheckHash will succeed.
// Assert.NoThrows
cms.CheckHash();
// Assert.NoThrows
cms.CheckSignature(true);
}
[ConditionalFact(typeof(SlhDsa), nameof(SlhDsa.IsSupported))]
public static void ReadSlhDsaDocument()
{

View File

@ -1823,6 +1823,175 @@ namespace System.Security.Cryptography.Pkcs.Tests
"77A5BF5851350C96F01142696CC1391632CB95C3370220017FD4D9329F00" +
"1EC74210CD34CAEE3878B2302602DB7930347E104679734291").HexToByteArray();
// produced with
// echo -n "Hello World!" | openssl cms -sign -signer ietfcert.pfx -md sha256 -nodetach -outform DER
internal static readonly byte[] MLDsa65_Sha256_SignedDocument = Convert.FromBase64String(
"""
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
JS0=
""");
// produced with
// echo -n "Hello World!" | openssl cms -sign -signer ietfcert.pfx -md sha256 -nodetach -outform DER
internal static readonly byte[] SlhDsa_Sha256_SignedDocument = Convert.FromBase64String(

View File

@ -619,46 +619,22 @@ namespace System.Security.Cryptography.Pkcs.Tests
[InlineData(SubjectIdentifierType.SubjectKeyIdentifier)]
public static void AddCounterSigner_RSA(SubjectIdentifierType identifierType)
{
SignedCms cms = new SignedCms();
cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
Assert.Single(cms.Certificates);
SignerInfo firstSigner = cms.SignerInfos[0];
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
firstSigner.ComputeCounterSignature(signer);
}
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
SignerInfo firstSigner2 = cms.SignerInfos[0];
Assert.Single(firstSigner2.CounterSignerInfos);
Assert.Single(firstSigner2.UnsignedAttributes);
SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0];
Assert.Equal(identifierType, counterSigner.SignerIdentifier.Type);
// On .NET Framework there will be two attributes, because Windows emits the
// content-type attribute even for counter-signers.
int expectedCount = 1;
#if NETFRAMEWORK
expectedCount = 2;
#endif
Assert.Equal(expectedCount, counterSigner.SignedAttributes.Count);
Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedCount - 1].Oid.Value);
Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate);
Assert.Equal(2, cms.Certificates.Count);
counterSigner.CheckSignature(true);
firstSigner2.CheckSignature(true);
cms.CheckSignature(true);
AssertAddCounterSigner(
identifierType,
signer =>
{
using (X509Certificate2 signerCert = Certificates.RSA2048SignatureOnly.TryGetCertificateWithPrivateKey())
{
CmsSigner counterSigner = new CmsSigner(identifierType, signerCert);
signer.ComputeCounterSignature(counterSigner);
}
},
(cms, counterSigner) =>
{
counterSigner.CheckSignature(true);
cms.SignerInfos[0].CheckSignature(true);
cms.CheckSignature(true);
});
}
[Fact]
@ -703,60 +679,33 @@ namespace System.Security.Cryptography.Pkcs.Tests
[SkipOnPlatform(PlatformSupport.MobileAppleCrypto, "DSA is not available")]
public static void AddCounterSigner_DSA()
{
SignedCms cms = new SignedCms();
cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
Assert.Single(cms.Certificates);
SignerInfo firstSigner = cms.SignerInfos[0];
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
using (X509Certificate2 signerCert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
// Best compatibility for DSA is SHA-1 (FIPS 186-2)
signer.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1);
firstSigner.ComputeCounterSignature(signer);
}
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
SignerInfo firstSigner2 = cms.SignerInfos[0];
Assert.Single(firstSigner2.CounterSignerInfos);
Assert.Single(firstSigner2.UnsignedAttributes);
Assert.Single(cms.SignerInfos);
Assert.Equal(2, cms.Certificates.Count);
SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0];
Assert.Equal(1, counterSigner.Version);
// On .NET Framework there will be two attributes, because Windows emits the
// content-type attribute even for counter-signers.
int expectedCount = 1;
#if NETFRAMEWORK
expectedCount = 2;
#endif
Assert.Equal(expectedCount, counterSigner.SignedAttributes.Count);
Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedCount - 1].Oid.Value);
Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate);
Assert.Equal(2, cms.Certificates.Count);
AssertAddCounterSigner(
SubjectIdentifierType.IssuerAndSerialNumber,
signer =>
{
using (X509Certificate2 signerCert = Certificates.Dsa1024.TryGetCertificateWithPrivateKey())
{
CmsSigner counterSigner = new CmsSigner(SubjectIdentifierType.IssuerAndSerialNumber, signerCert);
counterSigner.IncludeOption = X509IncludeOption.EndCertOnly;
// Best compatibility for DSA is SHA-1 (FIPS 186-2)
counterSigner.DigestAlgorithm = new Oid(Oids.Sha1, Oids.Sha1);
signer.ComputeCounterSignature(counterSigner);
}
},
(cms, counterSigner) =>
{
#if NET
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// DSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// DSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
#endif
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
});
}
[ConditionalTheory(typeof(SignatureSupport), nameof(SignatureSupport.SupportsRsaSha1Signatures))]
@ -771,63 +720,35 @@ namespace System.Security.Cryptography.Pkcs.Tests
[InlineData(SubjectIdentifierType.SubjectKeyIdentifier, Oids.Sha512)]
public static void AddCounterSigner_ECDSA(SubjectIdentifierType identifierType, string digestOid)
{
SignedCms cms = new SignedCms();
cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
Assert.Single(cms.Certificates);
SignerInfo firstSigner = cms.SignerInfos[0];
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
using (X509Certificate2 signerCert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
firstSigner.ComputeCounterSignature(signer);
}
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
SignerInfo firstSigner2 = cms.SignerInfos[0];
Assert.Single(firstSigner2.CounterSignerInfos);
Assert.Single(firstSigner2.UnsignedAttributes);
Assert.Single(cms.SignerInfos);
Assert.Equal(2, cms.Certificates.Count);
SignerInfo counterSigner = firstSigner2.CounterSignerInfos[0];
int expectedVersion = identifierType == SubjectIdentifierType.IssuerAndSerialNumber ? 1 : 3;
Assert.Equal(expectedVersion, counterSigner.Version);
// On .NET Framework there will be two attributes, because Windows emits the
// content-type attribute even for counter-signers.
int expectedCount = 1;
#if NETFRAMEWORK
expectedCount = 2;
#endif
Assert.Equal(expectedCount, counterSigner.SignedAttributes.Count);
Assert.Equal(Oids.MessageDigest, counterSigner.SignedAttributes[expectedCount - 1].Oid.Value);
Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate);
Assert.Equal(2, cms.Certificates.Count);
AssertAddCounterSigner(
identifierType,
signer =>
{
using (X509Certificate2 signerCert = Certificates.ECDsaP256Win.TryGetCertificateWithPrivateKey())
{
CmsSigner counterSigner = new CmsSigner(identifierType, signerCert);
counterSigner.IncludeOption = X509IncludeOption.EndCertOnly;
counterSigner.DigestAlgorithm = new Oid(digestOid, digestOid);
signer.ComputeCounterSignature(counterSigner);
}
},
(cms, counterSigner) =>
{
#if NET
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// DSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// DSA PKIX signature format is a DER SEQUENCE.
Assert.Equal(0x30, signature[0]);
// ECDSA Oids are all under 1.2.840.10045.4.
Assert.StartsWith("1.2.840.10045.4.", counterSigner.SignatureAlgorithm.Value);
// ECDSA Oids are all under 1.2.840.10045.4.
Assert.StartsWith("1.2.840.10045.4.", counterSigner.SignatureAlgorithm.Value);
#endif
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
});
}
public static IEnumerable<object[]> AddCounterSignerSlhDsaTestData =>
@ -849,6 +770,85 @@ namespace System.Security.Cryptography.Pkcs.Tests
[ConditionalTheory(nameof(SlhDsaAndRsaSha1SignaturesSupported))]
[MemberData(nameof(AddCounterSignerSlhDsaTestData))]
public static void AddCounterSigner_SlhDsa(SubjectIdentifierType identifierType, string digestOid, SlhDsaTestData.SlhDsaGeneratedKeyInfo info)
{
AssertAddCounterSigner(
identifierType,
signer =>
{
CertLoader loader = Certificates.SlhDsaGeneratedCerts.Single(cert => cert.CerData.SequenceEqual(info.Certificate));
using (X509Certificate2 signerCert = loader.TryGetCertificateWithPrivateKey())
{
CmsSigner counterSigner = new CmsSigner(identifierType, signerCert);
counterSigner.IncludeOption = X509IncludeOption.EndCertOnly;
counterSigner.DigestAlgorithm = new Oid(digestOid, digestOid);
signer.ComputeCounterSignature(counterSigner);
}
},
(cms, counterSigner) =>
{
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// SLH-DSA Oids are all under 2.16.840.1.101.3.4.3.
Assert.StartsWith("2.16.840.1.101.3.4.3.", counterSigner.SignatureAlgorithm.Value);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
});
}
public static IEnumerable<object[]> AddCounterSignerMLDsaTestData =>
from sit in new[] { SubjectIdentifierType.IssuerAndSerialNumber, SubjectIdentifierType.SubjectKeyIdentifier }
from data in new (MLDsaAlgorithm algorithm, string hashAlgorithm)[]
{
(MLDsaAlgorithm.MLDsa44, Oids.Shake128),
(MLDsaAlgorithm.MLDsa65, Oids.Sha512),
(MLDsaAlgorithm.MLDsa87, Oids.Shake256),
}
select new object[] { sit, data.hashAlgorithm, data.algorithm };
// TODO: Windows does not support draft 10 PKCS#8 format yet. Remove this and use MLDsa.IsSupported when it does.
private static bool SupportsDraft10Pkcs8 => MLDsa.IsSupported && !PlatformDetection.IsWindows;
public static bool MLDsaAndRsaSha1SignaturesSupported => SignatureSupport.SupportsRsaSha1Signatures && SupportsDraft10Pkcs8;
[ConditionalTheory(nameof(MLDsaAndRsaSha1SignaturesSupported))]
[MemberData(nameof(AddCounterSignerMLDsaTestData))]
public static void AddCounterSigner_MLDsa(SubjectIdentifierType identifierType, string digestOid, MLDsaAlgorithm algorithm)
{
AssertAddCounterSigner(
identifierType,
signer =>
{
using (X509Certificate2 signerCert = Certificates.MLDsaIetf[algorithm].TryGetCertificateWithPrivateKey())
{
CmsSigner counterSigner = new CmsSigner(identifierType, signerCert);
counterSigner.IncludeOption = X509IncludeOption.EndCertOnly;
counterSigner.DigestAlgorithm = new Oid(digestOid, digestOid);
signer.ComputeCounterSignature(counterSigner);
}
},
(cms, counterSigner) =>
{
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// ML-DSA Oids are all under 2.16.840.1.101.3.4.3.
Assert.StartsWith("2.16.840.1.101.3.4.3.", counterSigner.SignatureAlgorithm.Value);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
});
}
private static void AssertAddCounterSigner(
SubjectIdentifierType identifierType,
Action<SignerInfo> counterSignSigner,
Action<SignedCms, SignerInfo> assertCounterSigner)
{
SignedCms cms = new SignedCms();
cms.Decode(SignedDocuments.RsaPkcs1OneSignerIssuerAndSerialNumber);
@ -858,14 +858,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
CertLoader loader = Certificates.SlhDsaGeneratedCerts.Single(cert => cert.CerData.SequenceEqual(info.Certificate));
using (X509Certificate2 signerCert = loader.TryGetCertificateWithPrivateKey())
{
CmsSigner signer = new CmsSigner(identifierType, signerCert);
signer.IncludeOption = X509IncludeOption.EndCertOnly;
signer.DigestAlgorithm = new Oid(digestOid, digestOid);
firstSigner.ComputeCounterSignature(signer);
}
counterSignSigner(firstSigner);
Assert.Empty(firstSigner.CounterSignerInfos);
Assert.Empty(firstSigner.UnsignedAttributes);
@ -894,16 +887,7 @@ namespace System.Security.Cryptography.Pkcs.Tests
Assert.NotEqual(firstSigner2.Certificate, counterSigner.Certificate);
Assert.Equal(2, cms.Certificates.Count);
byte[] signature = counterSigner.GetSignature();
Assert.NotEmpty(signature);
// SLH-DSA Oids are all under 2.16.840.1.101.3.4.3.
Assert.StartsWith("2.16.840.1.101.3.4.3.", counterSigner.SignatureAlgorithm.Value);
cms.CheckSignature(true);
byte[] encoded = cms.Encode();
cms.Decode(encoded);
cms.CheckSignature(true);
assertCounterSigner(cms, counterSigner);
}
[ConditionalFact(typeof(SignatureSupport), nameof(SignatureSupport.SupportsRsaSha1Signatures))]

View File

@ -6,6 +6,10 @@
<NoWarn>$(NoWarn);SYSLIB5006</NoWarn>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\MLDsa\MLDsaTestsData.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\MLDsa\MLDsaTestsData.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\MLDsa\MLDsaTestsData.Ietf.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\MLDsa\MLDsaTestsData.Ietf.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\SlhDsa\SlhDsaTestData.cs"
Link="CommonTest\System\Security\Cryptography\AlgorithmImplementations\SlhDsa\SlhDsaTestData.cs" />
<Compile Include="$(CommonTestPath)System\Security\Cryptography\AlgorithmImplementations\SlhDsa\SlhDsaTestData.GeneratedCertificates.cs"