diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaCng.Windows.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaCng.Windows.cs new file mode 100644 index 00000000000000..a4ae67443b2c5c --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaCng.Windows.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Security.Cryptography +{ + public sealed partial class CompositeMLDsaCng : CompositeMLDsa + { + public partial CngKey GetKey() => + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); + + /// + protected override int SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination) => + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); + + /// + protected override bool TryExportCompositeMLDsaPrivateKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); + + /// + protected override bool TryExportCompositeMLDsaPublicKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); + + /// + protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); + + /// + protected override bool VerifyDataCore(ReadOnlySpan data, ReadOnlySpan context, ReadOnlySpan signature) => + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); + } +} diff --git a/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaCng.cs b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaCng.cs new file mode 100644 index 00000000000000..246109e6c8b2d6 --- /dev/null +++ b/src/libraries/Common/src/System/Security/Cryptography/CompositeMLDsaCng.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Diagnostics.CodeAnalysis; +using System.Runtime.Versioning; + +namespace System.Security.Cryptography +{ + /// + /// Provides a Cryptography Next Generation (CNG) implementation of Composite ML-DSA. + /// + /// + /// + /// Developers are encouraged to program against the base class, + /// rather than any specific derived class. + /// The derived classes are intended for interop with the underlying system + /// cryptographic libraries. + /// + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public sealed partial class CompositeMLDsaCng : CompositeMLDsa + { + /// + /// Initializes a new instance of the class by using the specified . + /// + /// + /// The key that will be used as input to the cryptographic operations performed by the current object. + /// + /// + /// is . + /// + /// + /// does not specify a Composite ML-DSA group. + /// + /// + /// Cryptography Next Generation (CNG) classes are not supported on this system. + /// + [SupportedOSPlatform("windows")] + public CompositeMLDsaCng(CngKey key) + : base(AlgorithmFromHandle(key)) + { + throw new PlatformNotSupportedException(); + } + + private static CompositeMLDsaAlgorithm AlgorithmFromHandle(CngKey key) => + throw new PlatformNotSupportedException(); + + /// + /// Gets a new representing the key used by the current instance. + /// + /// + /// This instance has been disposed. + /// + /// + /// This object is not the same as the one passed to , + /// if that constructor was used. However, it will point to the same CNG key. + /// + public partial CngKey GetKey(); + } +} diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.Forwards.cs b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.Forwards.cs index 7718316a03710a..94568e5fdb1b0b 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.Forwards.cs +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.Forwards.cs @@ -14,6 +14,7 @@ #if NET10_0_OR_GREATER [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.CompositeMLDsa))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.CompositeMLDsaAlgorithm))] +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.CompositeMLDsaCng))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.MLDsa))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.MLDsaAlgorithm))] [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.MLDsaCng))] diff --git a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj index 21d422abe5fa48..f34fbaa03c6f13 100644 --- a/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj +++ b/src/libraries/Microsoft.Bcl.Cryptography/src/Microsoft.Bcl.Cryptography.csproj @@ -497,6 +497,10 @@ Link="Common\System\Security\Cryptography\CngHelpers.SignVerify.cs" /> + + + /// Gets the public key from this certificate. + /// + /// + /// The X.509 certificate that contains the public key. + /// + /// + /// The public key, or if this certificate does not have a Composite ML-DSA public key. + /// + /// + /// is . + /// + /// + /// The certificate has a Composite ML-DSA public key, but the platform does not support Composite ML-DSA. + /// + /// + /// The public key was invalid, or otherwise could not be imported. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public static CompositeMLDsa? GetCompositeMLDsaPublicKey(this X509Certificate2 certificate) + { + ArgumentNullException.ThrowIfNull(certificate); + +#if NET10_0_OR_GREATER + return certificate.GetCompositeMLDsaPublicKey(); +#else + if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(certificate.GetKeyAlgorithm()) is null) + { + return null; + } + + ArraySegment encoded = GetCertificateSubjectPublicKeyInfo(certificate); + + try + { + return CompositeMLDsa.ImportSubjectPublicKeyInfo(encoded); + } + finally + { + // SubjectPublicKeyInfo does not need to clear since it's public + CryptoPool.Return(encoded, clearSize: 0); + } +#endif + } + + /// + /// Gets the private key from this certificate. + /// + /// + /// The X.509 certificate that contains the private key. + /// + /// + /// The private key, or if this certificate does not have a Composite ML-DSA private key. + /// + /// + /// is . + /// + /// + /// Retrieving a Composite ML-DSA private key from a certificate is not supported on this platform. + /// + /// + /// An error occurred accessing the private key. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public static CompositeMLDsa? GetCompositeMLDsaPrivateKey(this X509Certificate2 certificate) + { + ArgumentNullException.ThrowIfNull(certificate); + +#if NET10_0_OR_GREATER + return certificate.GetCompositeMLDsaPrivateKey(); +#else + if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(certificate.GetKeyAlgorithm()) is null) + { + return null; + } + + throw new PlatformNotSupportedException(); +#endif + } + + /// + /// Combines a private key with a certificate containing the associated public key into a + /// new instance that can access the private key. + /// + /// + /// The X.509 certificate that contains the public key. + /// + /// + /// The Composite ML-DSA private key that corresponds to the Composite ML-DSA public key in this certificate. + /// + /// + /// A new certificate with the property set to . + /// The current certificate isn't modified. + /// + /// + /// or is . + /// + /// + /// The specified private key doesn't match the public key for this certificate. + /// + /// + /// The certificate already has an associated private key. + /// + /// + /// Combining a certificate and a Composite ML-DSA private key is not supported on this platform. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public static X509Certificate2 CopyWithPrivateKey(this X509Certificate2 certificate, CompositeMLDsa privateKey) + { + ArgumentNullException.ThrowIfNull(certificate); + ArgumentNullException.ThrowIfNull(privateKey); + +#if NET10_0_OR_GREATER + return certificate.CopyWithPrivateKey(privateKey); +#elif NETSTANDARD + throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(CompositeMLDsa))); +#else + if (!Helpers.IsOSPlatformWindows) + throw new PlatformNotSupportedException(); + + if (certificate.HasPrivateKey) + throw new InvalidOperationException(SR.Cryptography_Cert_AlreadyHasPrivateKey); + + using (CompositeMLDsa? publicKey = GetCompositeMLDsaPublicKey(certificate)) + { + if (publicKey is null) + { + throw new ArgumentException(SR.Cryptography_PrivateKey_WrongAlgorithm); + } + + if (publicKey.Algorithm != privateKey.Algorithm) + { + throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey)); + } + + byte[] pk1 = publicKey.ExportCompositeMLDsaPublicKey(); + byte[] pk2 = privateKey.ExportCompositeMLDsaPublicKey(); + + if (!pk1.SequenceEqual(pk2)) + { + throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey)); + } + } + + throw new PlatformNotSupportedException(); +#endif + } + #if !NET10_0_OR_GREATER private static ArraySegment GetCertificateSubjectPublicKeyInfo(X509Certificate2 certificate) { diff --git a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netstandard21.cs b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netstandard21.cs index 16dd2454d71cbd..a28c452a5beeb9 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netstandard21.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/ref/System.Security.Cryptography.Pkcs.netstandard21.cs @@ -16,6 +16,8 @@ public sealed partial class CmsSigner { 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.CompositeMLDsa? 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.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}")] diff --git a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs index b67de43a39d457..312e25dc1affc5 100644 --- a/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs +++ b/src/libraries/System.Security.Cryptography.Pkcs/src/System/Security/Cryptography/Pkcs/CmsSigner.cs @@ -133,6 +133,18 @@ public CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? c { } +#if NET || NETSTANDARD2_1 + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public +#else + private +#endif + CmsSigner(SubjectIdentifierType signerIdentifierType, X509Certificate2? certificate, CompositeMLDsa? privateKey) + : this(signerIdentifierType, certificate, privateKey, signaturePadding: null) + { + throw new PlatformNotSupportedException(); + } + /// /// Initializes a new instance of the CmsSigner class with a specified signer /// certificate, subject identifier type, private key object, and RSA signature padding. diff --git a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs index abf49cf53be5cd..60e3360c64741a 100644 --- a/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs +++ b/src/libraries/System.Security.Cryptography/ref/System.Security.Cryptography.cs @@ -646,6 +646,18 @@ internal CompositeMLDsaAlgorithm() { } public static bool operator !=(System.Security.Cryptography.CompositeMLDsaAlgorithm? left, System.Security.Cryptography.CompositeMLDsaAlgorithm? right) { throw null; } public override string ToString() { throw null; } } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public sealed partial class CompositeMLDsaCng : System.Security.Cryptography.CompositeMLDsa + { + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] + public CompositeMLDsaCng(System.Security.Cryptography.CngKey key) : base (default(System.Security.Cryptography.CompositeMLDsaAlgorithm)) { } + public System.Security.Cryptography.CngKey GetKey() { throw null; } + protected override int SignDataCore(System.ReadOnlySpan data, System.ReadOnlySpan context, System.Span destination) { throw null; } + protected override bool TryExportCompositeMLDsaPrivateKeyCore(System.Span destination, out int bytesWritten) { throw null; } + protected override bool TryExportCompositeMLDsaPublicKeyCore(System.Span destination, out int bytesWritten) { throw null; } + protected override bool TryExportPkcs8PrivateKeyCore(System.Span destination, out int bytesWritten) { throw null; } + protected override bool VerifyDataCore(System.ReadOnlySpan data, System.ReadOnlySpan context, System.ReadOnlySpan signature) { throw null; } + } public partial class CryptoConfig { public CryptoConfig() { } @@ -3350,6 +3362,8 @@ namespace System.Security.Cryptography.X509Certificates [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public sealed partial class CertificateRequest { + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.CompositeMLDsa key) { } public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.ECDsa key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.MLDsa key) { } @@ -3358,6 +3372,8 @@ public CertificateRequest(System.Security.Cryptography.X509Certificates.X500Dist public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.SlhDsa key) { } public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.X509Certificates.PublicKey publicKey, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { } public CertificateRequest(System.Security.Cryptography.X509Certificates.X500DistinguishedName subjectName, System.Security.Cryptography.X509Certificates.PublicKey publicKey, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding? rsaSignaturePadding = null) { } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public CertificateRequest(string subjectName, System.Security.Cryptography.CompositeMLDsa key) { } public CertificateRequest(string subjectName, System.Security.Cryptography.ECDsa key, System.Security.Cryptography.HashAlgorithmName hashAlgorithm) { } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public CertificateRequest(string subjectName, System.Security.Cryptography.MLDsa key) { } @@ -3464,6 +3480,8 @@ public sealed partial class PublicKey { public PublicKey(System.Security.Cryptography.AsymmetricAlgorithm key) { } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006")] + public PublicKey(System.Security.Cryptography.CompositeMLDsa key) { } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006")] public PublicKey(System.Security.Cryptography.MLDsa key) { } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public PublicKey(System.Security.Cryptography.MLKem key) { } @@ -3477,6 +3495,9 @@ public PublicKey(System.Security.Cryptography.SlhDsa key) { } public System.Security.Cryptography.Oid Oid { get { throw null; } } public static System.Security.Cryptography.X509Certificates.PublicKey CreateFromSubjectPublicKeyInfo(System.ReadOnlySpan source, out int bytesRead) { throw null; } public byte[] ExportSubjectPublicKeyInfo() { throw null; } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + public System.Security.Cryptography.CompositeMLDsa? GetCompositeMLDsaPublicKey() { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("ios")] [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("tvos")] @@ -3804,6 +3825,8 @@ public X509Certificate2(string fileName, string? password, System.Security.Crypt public System.Security.Cryptography.X509Certificates.X500DistinguishedName SubjectName { get { throw null; } } public string Thumbprint { get { throw null; } } public int Version { get { throw null; } } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public System.Security.Cryptography.X509Certificates.X509Certificate2 CopyWithPrivateKey(System.Security.Cryptography.CompositeMLDsa privateKey) { throw null; } public System.Security.Cryptography.X509Certificates.X509Certificate2 CopyWithPrivateKey(System.Security.Cryptography.ECDiffieHellman privateKey) { throw null; } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public System.Security.Cryptography.X509Certificates.X509Certificate2 CopyWithPrivateKey(System.Security.Cryptography.MLDsa privateKey) { throw null; } @@ -3828,6 +3851,10 @@ public X509Certificate2(string fileName, string? password, System.Security.Crypt public static System.Security.Cryptography.X509Certificates.X509ContentType GetCertContentType(System.ReadOnlySpan rawData) { throw null; } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static System.Security.Cryptography.X509Certificates.X509ContentType GetCertContentType(string fileName) { throw null; } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public System.Security.Cryptography.CompositeMLDsa? GetCompositeMLDsaPrivateKey() { throw null; } + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public System.Security.Cryptography.CompositeMLDsa? GetCompositeMLDsaPublicKey() { throw null; } public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPrivateKey() { throw null; } public System.Security.Cryptography.ECDiffieHellman? GetECDiffieHellmanPublicKey() { throw null; } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] @@ -4230,6 +4257,8 @@ public abstract partial class X509SignatureGenerator protected X509SignatureGenerator() { } public System.Security.Cryptography.X509Certificates.PublicKey PublicKey { get { throw null; } } protected abstract System.Security.Cryptography.X509Certificates.PublicKey BuildPublicKey(); + [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] + public static System.Security.Cryptography.X509Certificates.X509SignatureGenerator CreateForCompositeMLDsa(System.Security.Cryptography.CompositeMLDsa key) { throw null; } public static System.Security.Cryptography.X509Certificates.X509SignatureGenerator CreateForECDsa(System.Security.Cryptography.ECDsa key) { throw null; } [System.Diagnostics.CodeAnalysis.ExperimentalAttribute("SYSLIB5006", UrlFormat="https://aka.ms/dotnet-warnings/{0}")] public static System.Security.Cryptography.X509Certificates.X509SignatureGenerator CreateForMLDsa(System.Security.Cryptography.MLDsa key) { throw null; } diff --git a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj index d732f3a02a5e64..b7ef41c439c00f 100644 --- a/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj +++ b/src/libraries/System.Security.Cryptography/src/System.Security.Cryptography.csproj @@ -382,6 +382,8 @@ Link="Common\System\Security\Cryptography\CompositeMLDsa.cs" /> + + destination, out throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); } } + + public sealed partial class CompositeMLDsaCng : CompositeMLDsa + { + public partial CngKey GetKey() => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); + + protected override int SignDataCore(ReadOnlySpan data, ReadOnlySpan context, Span destination) => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); + + protected override bool TryExportCompositeMLDsaPrivateKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); + + protected override bool TryExportCompositeMLDsaPublicKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); + + protected override bool TryExportPkcs8PrivateKeyCore(Span destination, out int bytesWritten) => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); + + protected override bool VerifyDataCore(ReadOnlySpan data, ReadOnlySpan context, ReadOnlySpan signature) => + throw new PlatformNotSupportedException(SR.PlatformNotSupported_CryptographyCng); + } } diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs index 28d07f1e26d075..3b021b2e6ead09 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/CertificateRequest.cs @@ -291,6 +291,54 @@ public CertificateRequest( PublicKey = _generator.PublicKey; } + /// + /// Create a CertificateRequest for the specified subject name and Composite ML-DSA key. + /// + /// + /// The parsed representation of the subject name for the certificate or certificate request. + /// + /// + /// A Composite ML-DSA key whose public key material will be included in the certificate or certificate request. + /// This key will be used as a private key if is called. + /// + /// + /// or is . + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public CertificateRequest( + string subjectName, + CompositeMLDsa key) + { + ArgumentNullException.ThrowIfNull(subjectName); + ArgumentNullException.ThrowIfNull(key); + + throw new PlatformNotSupportedException(); + } + + /// + /// Create a CertificateRequest for the specified subject name and Composite ML-DSA key. + /// + /// + /// The parsed representation of the subject name for the certificate or certificate request. + /// + /// + /// A Composite ML-DSA key whose public key material will be included in the certificate or certificate request. + /// This key will be used as a private key if is called. + /// + /// + /// or is . + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public CertificateRequest( + X500DistinguishedName subjectName, + CompositeMLDsa key) + { + ArgumentNullException.ThrowIfNull(subjectName); + ArgumentNullException.ThrowIfNull(key); + + throw new PlatformNotSupportedException(); + } + /// /// Create a CertificateRequest for the specified subject name, encoded public key, and hash algorithm. /// diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs index 5acedece549abb..fd9c92a2f77ca8 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/PublicKey.cs @@ -108,6 +108,23 @@ public PublicKey(SlhDsa key) : this(key.ExportSubjectPublicKeyInfo()) { } + /// + /// Initializes a new instance of the class + /// using SubjectPublicKeyInfo from an . + /// + /// + /// An key to obtain the SubjectPublicKeyInfo from. + /// + /// + /// The SubjectPublicKeyInfo could not be decoded. The + /// must return a + /// valid ASN.1-DER encoded X.509 SubjectPublicKeyInfo. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId)] + public PublicKey(CompositeMLDsa key) : this(key.ExportSubjectPublicKeyInfo()) + { + } + private PublicKey(byte[] subjectPublicKeyInfo) { DecodeSubjectPublicKeyInfo( @@ -394,6 +411,29 @@ public static PublicKey CreateFromSubjectPublicKeyInfo(ReadOnlySpan source ? EncodeSubjectPublicKeyInfo().Encode(SlhDsa.ImportSubjectPublicKeyInfo) : null; + /// + /// Gets the public key, or + /// if the key is not a Composite ML-DSA key. + /// + /// + /// The public key, or if the key is not a Composite ML-DSA key. + /// + /// + /// The object represents a Composite ML-DSA public key, but the platform does not support the algorithm. + /// + /// + /// The key contents are corrupt or could not be read successfully. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + [UnsupportedOSPlatform("browser")] + public CompositeMLDsa? GetCompositeMLDsaPublicKey() + { + if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(_oid.Value) is null) + return null; + + return EncodeSubjectPublicKeyInfo().Encode(CompositeMLDsa.ImportSubjectPublicKeyInfo); + } + internal AsnWriter EncodeSubjectPublicKeyInfo() { SubjectPublicKeyInfoAsn spki = new SubjectPublicKeyInfoAsn diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs index 373d95205d54df..d2b9b688e5d449 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509Certificate2.cs @@ -1086,6 +1086,108 @@ public X509Certificate2 CopyWithPrivateKey(SlhDsa privateKey) return new X509Certificate2(pal); } + /// + /// Gets the public key from this certificate. + /// + /// + /// The public key, or if this certificate does not have a Composite ML-DSA public key. + /// + /// + /// The certificate has a Composite ML-DSA public key, but the platform does not support Composite ML-DSA. + /// + /// + /// The public key was invalid, or otherwise could not be imported. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public CompositeMLDsa? GetCompositeMLDsaPublicKey() + { + if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(GetKeyAlgorithm()) is null) + { + return null; + } + + Debug.Assert(!OperatingSystem.IsBrowser()); + return PublicKey.GetCompositeMLDsaPublicKey(); + } + + /// + /// Gets the private key from this certificate. + /// + /// + /// The private key, or if this certificate does not have a Composite ML-DSA private key. + /// + /// + /// Retrieving a Composite ML-DSA private key from a certificate is not supported on this platform. + /// + /// + /// An error occurred accessing the private key. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public CompositeMLDsa? GetCompositeMLDsaPrivateKey() + { + if (CompositeMLDsaAlgorithm.GetAlgorithmFromOid(GetKeyAlgorithm()) is null) + { + return null; + } + + throw new PlatformNotSupportedException(); + } + + /// + /// Combines a private key with a certificate containing the associated public key into a + /// new instance that can access the private key. + /// + /// + /// The Composite ML-DSA private key that corresponds to the Composite ML-DSA public key in this certificate. + /// + /// + /// A new certificate with the property set to . + /// The current certificate isn't modified. + /// + /// + /// is . + /// + /// + /// The specified private key doesn't match the public key for this certificate. + /// + /// + /// The certificate already has an associated private key. + /// + /// + /// Combining a certificate and a Composite ML-DSA private key is not supported on this platform. + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public X509Certificate2 CopyWithPrivateKey(CompositeMLDsa privateKey) + { + ArgumentNullException.ThrowIfNull(privateKey); + + if (HasPrivateKey) + throw new InvalidOperationException(SR.Cryptography_Cert_AlreadyHasPrivateKey); + + using (CompositeMLDsa? publicKey = GetCompositeMLDsaPublicKey()) + { + if (publicKey is null) + { + throw new ArgumentException(SR.Cryptography_PrivateKey_WrongAlgorithm); + } + + if (publicKey.Algorithm != privateKey.Algorithm) + { + throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey)); + } + + byte[] pk1 = publicKey.ExportCompositeMLDsaPublicKey(); + byte[] pk2 = privateKey.ExportCompositeMLDsaPublicKey(); + + if (!pk1.SequenceEqual(pk2)) + { + throw new ArgumentException(SR.Cryptography_PrivateKey_DoesNotMatch, nameof(privateKey)); + } + } + + throw new PlatformNotSupportedException(); + } + /// /// Creates a new X509 certificate from the file contents of an RFC 7468 PEM-encoded /// certificate and private key. diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SignatureGenerator.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SignatureGenerator.cs index 3d4e1b9763eed6..065330f56864d6 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SignatureGenerator.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X509SignatureGenerator.cs @@ -74,5 +74,25 @@ public static X509SignatureGenerator CreateForSlhDsa(SlhDsa key) return new SlhDsaX509SignatureGenerator(key); } + + /// + /// Creates a signature generator for Composite ML-DSA signatures using the specified key. + /// + /// + /// The private key. + /// + /// + /// An object for Composite ML-DSA signatures. + /// + /// + /// is . + /// + [Experimental(Experimentals.PostQuantumCryptographyDiagId, UrlFormat = Experimentals.SharedUrlFormat)] + public static X509SignatureGenerator CreateForCompositeMLDsa(CompositeMLDsa key) + { + ArgumentNullException.ThrowIfNull(key); + + throw new PlatformNotSupportedException(); + } } }