// 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; using System.Runtime.InteropServices; using Internal.Cryptography; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates { internal sealed partial class CertificatePal : IDisposable, ICertificatePal { public bool HasPrivateKey { get { return _certContext.ContainsPrivateKey; } } public RSA? GetRSAPrivateKey() { return GetPrivateKey<RSA>( delegate (CspParameters csp) { return new RSACryptoServiceProvider(csp); }, delegate (CngKey cngKey) { return new RSACng(cngKey, transferOwnership: true); } ); } public DSA? GetDSAPrivateKey() { return GetPrivateKey<DSA>( delegate (CspParameters csp) { return new DSACryptoServiceProvider(csp); }, delegate (CngKey cngKey) { return new DSACng(cngKey, transferOwnership: true); } ); } public ECDsa? GetECDsaPrivateKey() { return GetPrivateKey<ECDsa>( delegate (CspParameters csp) { throw new NotSupportedException(SR.NotSupported_ECDsa_Csp); }, delegate (CngKey cngKey) { return new ECDsaCng(cngKey, transferOwnership: true); } ); } public ECDiffieHellman? GetECDiffieHellmanPrivateKey() { static ECDiffieHellmanCng? FromCngKey(CngKey cngKey) { if (cngKey.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman) { return new ECDiffieHellmanCng(cngKey, transferOwnership: true); } // We might be getting an ECDSA key here. CNG allows ECDH to be either ECDH or ECDSA, however if // the AlgorithmGroup is ECDSA, then it cannot be used for ECDH, even though both of them are ECC keys. return null; } return GetPrivateKey<ECDiffieHellman>( csp => throw new NotSupportedException(SR.NotSupported_ECDiffieHellman_Csp), FromCngKey ); } public MLDsa? GetMLDsaPrivateKey() { return GetPrivateKey<MLDsa>( _ => { Debug.Fail("CryptoApi does not support ML-DSA."); throw new PlatformNotSupportedException(); }, cngKey => new MLDsaCng(cngKey, transferOwnership: true) ); } public MLKem? GetMLKemPrivateKey() { // MLKem is not supported on Windows. return null; } public SlhDsa? GetSlhDsaPrivateKey() { // SlhDsa is not supported on Windows. return null; } public ICertificatePal CopyWithPrivateKey(DSA dsa) { DSACng? dsaCng = dsa as DSACng; ICertificatePal? clone; if (dsaCng != null) { clone = CopyWithPersistedCngKey(dsaCng.Key); if (clone != null) { return clone; } } DSACryptoServiceProvider? dsaCsp = dsa as DSACryptoServiceProvider; if (dsaCsp != null) { clone = CopyWithPersistedCapiKey(dsaCsp.CspKeyContainerInfo); if (clone != null) { return clone; } } DSAParameters privateParameters = dsa.ExportParameters(true); using (PinAndClear.Track(privateParameters.X!)) using (DSACng clonedKey = new DSACng()) { clonedKey.ImportParameters(privateParameters); return CopyWithEphemeralKey(clonedKey.Key); } } public ICertificatePal CopyWithPrivateKey(ECDsa ecdsa) { ECDsaCng? ecdsaCng = ecdsa as ECDsaCng; if (ecdsaCng != null) { ICertificatePal? clone = CopyWithPersistedCngKey(ecdsaCng.Key); if (clone != null) { return clone; } } ECParameters privateParameters = ecdsa.ExportParameters(true); using (PinAndClear.Track(privateParameters.D!)) using (ECDsaCng clonedKey = new ECDsaCng()) { clonedKey.ImportParameters(privateParameters); return CopyWithEphemeralKey(clonedKey.Key); } } public ICertificatePal CopyWithPrivateKey(ECDiffieHellman ecdh) { ECDiffieHellmanCng? ecdhCng = ecdh as ECDiffieHellmanCng; if (ecdhCng != null) { ICertificatePal? clone = CopyWithPersistedCngKey(ecdhCng.Key); if (clone != null) { return clone; } } ECParameters privateParameters = ecdh.ExportParameters(true); using (PinAndClear.Track(privateParameters.D!)) using (ECDiffieHellmanCng clonedKey = new ECDiffieHellmanCng()) { clonedKey.ImportParameters(privateParameters); return CopyWithEphemeralKey(clonedKey.Key); } } public ICertificatePal CopyWithPrivateKey(MLDsa privateKey) => CertificateHelpers.CopyWithPrivateKey(this, privateKey); public ICertificatePal CopyWithPrivateKey(MLKem privateKey) { throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(MLKem))); } public ICertificatePal CopyWithPrivateKey(SlhDsa privateKey) { throw new PlatformNotSupportedException(SR.Format(SR.Cryptography_AlgorithmNotSupported, nameof(SlhDsa))); } public ICertificatePal CopyWithPrivateKey(RSA rsa) { RSACng? rsaCng = rsa as RSACng; ICertificatePal? clone; if (rsaCng != null) { clone = CopyWithPersistedCngKey(rsaCng.Key); if (clone != null) { return clone; } } RSACryptoServiceProvider? rsaCsp = rsa as RSACryptoServiceProvider; if (rsaCsp != null) { clone = CopyWithPersistedCapiKey(rsaCsp.CspKeyContainerInfo); if (clone != null) { return clone; } } RSAParameters privateParameters = rsa.ExportParameters(true); using (PinAndClear.Track(privateParameters.D!)) using (PinAndClear.Track(privateParameters.P!)) using (PinAndClear.Track(privateParameters.Q!)) using (PinAndClear.Track(privateParameters.DP!)) using (PinAndClear.Track(privateParameters.DQ!)) using (PinAndClear.Track(privateParameters.InverseQ!)) using (RSACng clonedKey = new RSACng()) { clonedKey.ImportParameters(privateParameters); return CopyWithEphemeralKey(clonedKey.Key); } } private unsafe CertificatePal? CopyWithPersistedCapiKey(CspKeyContainerInfo keyContainerInfo) { if (string.IsNullOrEmpty(keyContainerInfo.KeyContainerName)) { return null; } // Make a new pal from bytes. CertificatePal pal = (CertificatePal)FromBlob(RawData, SafePasswordHandle.InvalidHandle, X509KeyStorageFlags.PersistKeySet); Interop.Crypt32.CRYPT_KEY_PROV_INFO keyProvInfo = default; fixed (char* keyName = keyContainerInfo.KeyContainerName) fixed (char* provName = keyContainerInfo.ProviderName) { keyProvInfo.pwszContainerName = keyName; keyProvInfo.pwszProvName = provName; keyProvInfo.dwFlags = keyContainerInfo.MachineKeyStore ? Interop.Crypt32.CryptAcquireContextFlags.CRYPT_MACHINE_KEYSET : 0; keyProvInfo.dwProvType = keyContainerInfo.ProviderType; keyProvInfo.dwKeySpec = (int)keyContainerInfo.KeyNumber; if (!Interop.Crypt32.CertSetCertificateContextProperty( pal._certContext, Interop.Crypt32.CertContextPropId.CERT_KEY_PROV_INFO_PROP_ID, Interop.Crypt32.CertSetPropertyFlags.None, &keyProvInfo)) { Exception e = Marshal.GetLastPInvokeError().ToCryptographicException(); pal.Dispose(); throw e; } } return pal; } private T? GetPrivateKey<T>(Func<CspParameters, T> createCsp, Func<CngKey, T?> createCng) where T : class, IDisposable { return CertificateHelpers.GetPrivateKey<T>(this, createCsp, createCng); } private CertificatePal? CopyWithPersistedCngKey(CngKey cngKey) => CertificateHelpers.CopyWithPersistedCngKey(this, cngKey); private CertificatePal CopyWithEphemeralKey(CngKey cngKey) => CertificateHelpers.CopyWithEphemeralKey(this, cngKey); } } |