| File: System\Security\Cryptography\X509Certificates\CertificateHelpers.Windows.cs | Web Access |
| Project: src\runtime\src\libraries\System.Security.Cryptography\src\System.Security.Cryptography.csproj (System.Security.Cryptography) |
// 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 static partial class CertificateHelpers { private static partial CryptographicException GetExceptionForLastError() => Marshal.GetLastPInvokeError().ToCryptographicException(); private static partial CertificatePal CopyFromRawBytes(CertificatePal certificate) => (CertificatePal)CertificatePal.FromBlob(certificate.RawData, SafePasswordHandle.InvalidHandle, X509KeyStorageFlags.PersistKeySet); private static partial SafeNCryptKeyHandle CreateSafeNCryptKeyHandle(IntPtr handle, SafeHandle parentHandle) => new SafeNCryptKeyHandle(handle, parentHandle); private static partial int GuessKeySpec( CngProvider provider, string keyName, bool machineKey, CngAlgorithmGroup? algorithmGroup) { if (provider == CngProvider.MicrosoftSoftwareKeyStorageProvider || provider == CngProvider.MicrosoftSmartCardKeyStorageProvider) { // Well-known CNG providers, keySpec is 0. return 0; } try { CngKeyOpenOptions options = machineKey ? CngKeyOpenOptions.MachineKey : CngKeyOpenOptions.None; using (CngKey.Open(keyName, provider, options)) { // It opened with keySpec 0, so use keySpec 0. return 0; } } catch (CryptographicException) { // While NTE_BAD_KEYSET is what we generally expect here for RSA, on Windows 7 // PROV_DSS produces NTE_BAD_PROV_TYPE, and PROV_DSS_DH produces NTE_NO_KEY. // // So we'll just try the CAPI fallback for any error code, and see what happens. CspParameters cspParameters = new CspParameters { ProviderName = provider.Provider, KeyContainerName = keyName, Flags = CspProviderFlags.UseExistingKey, KeyNumber = (int)KeyNumber.Signature, }; if (machineKey) { cspParameters.Flags |= CspProviderFlags.UseMachineKeyStore; } if (TryGuessKeySpec(cspParameters, algorithmGroup, out int keySpec)) { return keySpec; } throw; } } private static bool TryGuessKeySpec( CspParameters cspParameters, CngAlgorithmGroup? algorithmGroup, out int keySpec) { if (algorithmGroup == CngAlgorithmGroup.Rsa) { return TryGuessRsaKeySpec(cspParameters, out keySpec); } if (algorithmGroup == CngAlgorithmGroup.Dsa) { return TryGuessDsaKeySpec(cspParameters, out keySpec); } keySpec = 0; return false; } private static bool TryGuessRsaKeySpec(CspParameters cspParameters, out int keySpec) { // Try the AT_SIGNATURE spot in each of the 4 RSA provider type values, // ideally one of them will work. const int PROV_RSA_FULL = 1; const int PROV_RSA_SIG = 2; const int PROV_RSA_SCHANNEL = 12; const int PROV_RSA_AES = 24; // These are ordered in terms of perceived likeliness, given that the key // is AT_SIGNATURE. ReadOnlySpan<int> provTypes = [ PROV_RSA_FULL, PROV_RSA_AES, PROV_RSA_SCHANNEL, // Nothing should be PROV_RSA_SIG, but if everything else has failed, // just try this last thing. PROV_RSA_SIG, ]; foreach (int provType in provTypes) { cspParameters.ProviderType = provType; try { using (new RSACryptoServiceProvider(cspParameters)) { keySpec = cspParameters.KeyNumber; return true; } } catch (CryptographicException) { } } Debug.Fail("RSA key did not open with KeyNumber 0 or AT_SIGNATURE"); keySpec = 0; return false; } private static bool TryGuessDsaKeySpec(CspParameters cspParameters, out int keySpec) { const int PROV_DSS = 3; const int PROV_DSS_DH = 13; ReadOnlySpan<int> provTypes = [ PROV_DSS_DH, PROV_DSS, ]; foreach (int provType in provTypes) { cspParameters.ProviderType = provType; try { using (new DSACryptoServiceProvider(cspParameters)) { keySpec = cspParameters.KeyNumber; return true; } } catch (CryptographicException) { } } Debug.Fail("DSA key did not open with KeyNumber 0 or AT_SIGNATURE"); keySpec = 0; return false; } private static partial SafeCertContextHandle DuplicateCertificateHandle(CertificatePal certificate) { SafeCertContextHandle? handle = certificate.SafeHandle; bool addedRef = false; try { if (handle is not null) { handle.DangerousAddRef(ref addedRef); return Interop.Crypt32.CertDuplicateCertificateContext(handle.DangerousGetHandle()); } } catch (ObjectDisposedException) { // Let this go to the invalid handle throw. } finally { if (addedRef) { handle!.DangerousRelease(); } } throw new CryptographicException(SR.Format(SR.Cryptography_InvalidHandle, nameof(handle))); } } }