mirror of https://github.com/dotnet/runtime
Merge 639fc3bf71
into 123627ba0f
This commit is contained in:
commit
c2f1f5d21a
|
@ -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);
|
||||
|
|
|
@ -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 { } }
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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)));
|
||||
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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}"),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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))]
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue