public sealed override ContentInfo TryDecrypt(RecipientInfo recipientInfo, X509Certificate2 cert, X509Certificate2Collection originatorCerts, X509Certificate2Collection extraStore, out Exception exception) { Debug.Assert(recipientInfo != null); Debug.Assert(cert != null); Debug.Assert(originatorCerts != null); Debug.Assert(extraStore != null); CryptKeySpec keySpec; exception = TryGetKeySpecForCertificate(cert, out keySpec); if (exception != null) { return(null); } // Desktop compat: We pass false for "silent" here (thus allowing crypto providers to display UI.) const bool Silent = false; // Note: Using CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG rather than CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG // because wrapping an NCrypt wrapper over CAPI keys unconditionally causes some legacy features // (such as RC4 support) to break. const bool PreferNCrypt = false; using (SafeProvOrNCryptKeyHandle hKey = PkcsPalWindows.GetCertificatePrivateKey(cert, Silent, PreferNCrypt, out _, out exception)) { if (hKey == null) { return(null); } RecipientInfoType type = recipientInfo.Type; switch (type) { case RecipientInfoType.KeyTransport: exception = TryDecryptTrans((KeyTransRecipientInfo)recipientInfo, hKey, keySpec); break; case RecipientInfoType.KeyAgreement: exception = TryDecryptAgree((KeyAgreeRecipientInfo)recipientInfo, hKey, keySpec, originatorCerts, extraStore); break; default: // Since only the framework can construct RecipientInfo's, we're at fault if we get here. So it's okay to assert and throw rather than // returning to the caller. Debug.Fail($"Unexpected RecipientInfoType: {type}"); throw new NotSupportedException(); } if (exception != null) { return(null); } // If we got here, we successfully decrypted. Return the decrypted content. return(_hCryptMsg.GetContentInfo()); } }
public sealed override ContentInfo TryDecrypt(RecipientInfo recipientInfo, X509Certificate2 cert, X509Certificate2Collection originatorCerts, X509Certificate2Collection extraStore, out Exception exception) { Debug.Assert(recipientInfo != null); Debug.Assert(cert != null); Debug.Assert(originatorCerts != null); Debug.Assert(extraStore != null); CryptKeySpec keySpec; exception = TryGetKeySpecForCertificate(cert, out keySpec); if (exception != null) { return(null); } // Desktop compat: We pass false for "silent" here (thus allowing crypto providers to display UI.) using (SafeProvOrNCryptKeyHandle hKey = TryGetCertificatePrivateKey(cert, false, out exception)) { if (hKey == null) { return(null); } RecipientInfoType type = recipientInfo.Type; switch (type) { case RecipientInfoType.KeyTransport: exception = TryDecryptTrans((KeyTransRecipientInfo)recipientInfo, hKey, keySpec); break; case RecipientInfoType.KeyAgreement: exception = TryDecryptAgree((KeyAgreeRecipientInfo)recipientInfo, hKey, keySpec, originatorCerts, extraStore); break; default: // Since only the framework can construct RecipientInfo's, we're at fault if we get here. So it's okay to assert and throw rather than // returning to the caller. Debug.Fail($"Unexpected RecipientInfoType: {type}"); throw new NotSupportedException(); } if (exception != null) { return(null); } // If we got here, we successfully decrypted. Return the decrypted content. return(_hCryptMsg.GetContentInfo()); } }
private static string GetStringProvParam( SafeProvOrNCryptKeyHandle handle, CryptProvParam dwParam, ref Span <byte> buf, ref byte[] rented, int clearLen) { int len = buf.Length; if (!Interop.Advapi32.CryptGetProvParam(handle, dwParam, buf, ref len)) { if (len > buf.Length) { ArrayPool <byte> pool = ArrayPool <byte> .Shared; if (rented != null) { Array.Clear(rented, 0, clearLen); pool.Return(rented); } rented = pool.Rent(len); buf = rented; len = rented.Length; } else { throw Marshal.GetLastWin32Error().ToCryptographicException(); } if (!Interop.Advapi32.CryptGetProvParam(handle, dwParam, buf, ref len)) { throw Marshal.GetLastWin32Error().ToCryptographicException(); } } unsafe { fixed(byte *asciiPtr = &MemoryMarshal.GetReference(buf)) { return(Marshal.PtrToStringAnsi((IntPtr)asciiPtr, len)); } } }
private Exception TryDecryptTrans(KeyTransRecipientInfo recipientInfo, SafeProvOrNCryptKeyHandle hKey, CryptKeySpec keySpec) { KeyTransRecipientInfoPalWindows pal = (KeyTransRecipientInfoPalWindows)(recipientInfo.Pal); CMSG_CTRL_DECRYPT_PARA decryptPara; decryptPara.cbSize = Marshal.SizeOf <CMSG_CTRL_DECRYPT_PARA>(); decryptPara.hKey = hKey; decryptPara.dwKeySpec = keySpec; decryptPara.dwRecipientIndex = pal.Index; bool success = Interop.Crypt32.CryptMsgControl(_hCryptMsg, 0, MsgControlType.CMSG_CTRL_DECRYPT, ref decryptPara); if (!success) { return(Marshal.GetHRForLastWin32Error().ToCryptographicException()); } return(null); }
private Exception TryDecryptAgree(KeyAgreeRecipientInfo keyAgreeRecipientInfo, SafeProvOrNCryptKeyHandle hKey, CryptKeySpec keySpec, X509Certificate2Collection originatorCerts, X509Certificate2Collection extraStore) { unsafe { KeyAgreeRecipientInfoPalWindows pal = (KeyAgreeRecipientInfoPalWindows)(keyAgreeRecipientInfo.Pal); return(pal.WithCmsgCmsRecipientInfo <Exception>( delegate(CMSG_KEY_AGREE_RECIPIENT_INFO * pKeyAgreeRecipientInfo) { CMSG_CTRL_KEY_AGREE_DECRYPT_PARA decryptPara = default(CMSG_CTRL_KEY_AGREE_DECRYPT_PARA); decryptPara.cbSize = Marshal.SizeOf <CMSG_CTRL_KEY_AGREE_DECRYPT_PARA>(); decryptPara.hProv = hKey; decryptPara.dwKeySpec = keySpec; decryptPara.pKeyAgree = pKeyAgreeRecipientInfo; decryptPara.dwRecipientIndex = pal.Index; decryptPara.dwRecipientEncryptedKeyIndex = pal.SubIndex; CMsgKeyAgreeOriginatorChoice originatorChoice = pKeyAgreeRecipientInfo->dwOriginatorChoice; switch (originatorChoice) { case CMsgKeyAgreeOriginatorChoice.CMSG_KEY_AGREE_ORIGINATOR_CERT: { X509Certificate2Collection candidateCerts = new X509Certificate2Collection(); candidateCerts.AddRange(Helpers.GetStoreCertificates(StoreName.AddressBook, StoreLocation.CurrentUser, openExistingOnly: true)); candidateCerts.AddRange(Helpers.GetStoreCertificates(StoreName.AddressBook, StoreLocation.LocalMachine, openExistingOnly: true)); candidateCerts.AddRange(originatorCerts); candidateCerts.AddRange(extraStore); SubjectIdentifier originatorId = pKeyAgreeRecipientInfo->OriginatorCertId.ToSubjectIdentifier(); X509Certificate2 originatorCert = candidateCerts.TryFindMatchingCertificate(originatorId); if (originatorCert == null) { return ErrorCode.CRYPT_E_NOT_FOUND.ToCryptographicException(); } using (SafeCertContextHandle hCertContext = originatorCert.CreateCertContextHandle()) { CERT_CONTEXT *pOriginatorCertContext = hCertContext.DangerousGetCertContext(); decryptPara.OriginatorPublicKey = pOriginatorCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey; // Do not factor this call out of the switch statement as leaving this "using" block will free up // native memory that decryptPara points to. return TryExecuteDecryptAgree(ref decryptPara); } } case CMsgKeyAgreeOriginatorChoice.CMSG_KEY_AGREE_ORIGINATOR_PUBLIC_KEY: { decryptPara.OriginatorPublicKey = pKeyAgreeRecipientInfo->OriginatorPublicKeyInfo.PublicKey; return TryExecuteDecryptAgree(ref decryptPara); } default: return new CryptographicException(SR.Format(SR.Cryptography_Cms_Invalid_Originator_Identifier_Choice, originatorChoice)); } })); } }
public unsafe sealed override ContentInfo TryDecrypt( RecipientInfo recipientInfo, X509Certificate2 cert, AsymmetricAlgorithm privateKey, X509Certificate2Collection originatorCerts, X509Certificate2Collection extraStore, out Exception exception) { Debug.Assert((cert != null) ^ (privateKey != null)); if (privateKey != null) { RSA key = privateKey as RSA; if (key == null) { exception = new CryptographicException(SR.Cryptography_Cms_Ktri_RSARequired); return(null); } ContentInfo contentInfo = _hCryptMsg.GetContentInfo(); byte[] cek = AnyOS.ManagedPkcsPal.ManagedKeyTransPal.DecryptCekCore( cert, key, recipientInfo.EncryptedKey, recipientInfo.KeyEncryptionAlgorithm.Oid.Value, out exception); // Pin CEK to prevent it from getting copied during heap compaction. fixed(byte *pinnedCek = cek) { try { if (exception != null) { return(null); } return(AnyOS.ManagedPkcsPal.ManagedDecryptorPal.TryDecryptCore( cek, contentInfo.ContentType.Value, contentInfo.Content, _contentEncryptionAlgorithm, out exception)); } finally { if (cek != null) { Array.Clear(cek, 0, cek.Length); } } } } Debug.Assert(recipientInfo != null); Debug.Assert(cert != null); Debug.Assert(originatorCerts != null); Debug.Assert(extraStore != null); CryptKeySpec keySpec; exception = TryGetKeySpecForCertificate(cert, out keySpec); if (exception != null) { return(null); } // Desktop compat: We pass false for "silent" here (thus allowing crypto providers to display UI.) const bool Silent = false; // Note: Using CRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAG rather than CRYPT_ACQUIRE_PREFER_NCRYPT_KEY_FLAG // because wrapping an NCrypt wrapper over CAPI keys unconditionally causes some legacy features // (such as RC4 support) to break. const bool PreferNCrypt = false; using (SafeProvOrNCryptKeyHandle hKey = PkcsPalWindows.GetCertificatePrivateKey(cert, Silent, PreferNCrypt, out _, out exception)) { if (hKey == null) { return(null); } RecipientInfoType type = recipientInfo.Type; switch (type) { case RecipientInfoType.KeyTransport: exception = TryDecryptTrans((KeyTransRecipientInfo)recipientInfo, hKey, keySpec); break; case RecipientInfoType.KeyAgreement: exception = TryDecryptAgree((KeyAgreeRecipientInfo)recipientInfo, hKey, keySpec, originatorCerts, extraStore); break; default: // Since only the framework can construct RecipientInfo's, we're at fault if we get here. So it's okay to assert and throw rather than // returning to the caller. Debug.Fail($"Unexpected RecipientInfoType: {type}"); throw new NotSupportedException(); } if (exception != null) { return(null); } // If we got here, we successfully decrypted. Return the decrypted content. return(_hCryptMsg.GetContentInfo()); } }
public static CspParameters GetProvParameters(this SafeProvOrNCryptKeyHandle handle) { // A normal key container name is a GUID (~34 bytes ASCII) // The longest standard provider name is 64 bytes (including the \0), // but we shouldn't have a CAPI call with a software CSP. // // In debug builds use a buffer which will need to be resized, but is big // enough to hold the DWORD "can't fail" values. Span <byte> stackSpan = stackalloc byte[ #if DEBUG sizeof(int) #else 64 #endif ]; stackSpan.Clear(); int size = stackSpan.Length; if (!Interop.Advapi32.CryptGetProvParam(handle, CryptProvParam.PP_PROVTYPE, stackSpan, ref size)) { throw Interop.CPError.GetLastWin32Error().ToCryptographicException(); } if (size != sizeof(int)) { Debug.Fail("PP_PROVTYPE writes a DWORD - enum misalignment?"); throw new CryptographicException(); } int provType = MemoryMarshal.Read <int>(stackSpan.Slice(0, size)); size = stackSpan.Length; if (!Interop.Advapi32.CryptGetProvParam(handle, CryptProvParam.PP_KEYSET_TYPE, stackSpan, ref size)) { throw Interop.CPError.GetLastWin32Error().ToCryptographicException(); } if (size != sizeof(int)) { Debug.Fail("PP_KEYSET_TYPE writes a DWORD - enum misalignment?"); throw new CryptographicException(); } int keysetType = MemoryMarshal.Read <int>(stackSpan.Slice(0, size)); // Only CRYPT_MACHINE_KEYSET is described as coming back, but be defensive. CspProviderFlags provFlags = ((CspProviderFlags)keysetType & CspProviderFlags.UseMachineKeyStore) | CspProviderFlags.UseExistingKey; byte[] rented = null; Span <byte> asciiStringBuf = stackSpan; string provName = GetStringProvParam(handle, CryptProvParam.PP_NAME, ref asciiStringBuf, ref rented, 0); int maxClear = provName.Length; string keyName = GetStringProvParam(handle, CryptProvParam.PP_CONTAINER, ref asciiStringBuf, ref rented, maxClear); maxClear = Math.Max(maxClear, keyName.Length); if (rented != null) { CryptoPool.Return(rented, maxClear); } return(new CspParameters(provType) { Flags = provFlags, KeyContainerName = keyName, ProviderName = provName, }); }
private unsafe Exception?TryDecryptTrans(KeyTransRecipientInfo recipientInfo, SafeProvOrNCryptKeyHandle hKey, CryptKeySpec keySpec) { KeyTransRecipientInfoPalWindows pal = (KeyTransRecipientInfoPalWindows)(recipientInfo.Pal); bool keyAddRefd = false; try { CMSG_CTRL_DECRYPT_PARA decryptPara; decryptPara.cbSize = sizeof(CMSG_CTRL_DECRYPT_PARA); hKey.DangerousAddRef(ref keyAddRefd); decryptPara.hKey = hKey.DangerousGetHandle(); decryptPara.dwKeySpec = keySpec; decryptPara.dwRecipientIndex = pal.Index; bool success = Interop.Crypt32.CryptMsgControl(_hCryptMsg, 0, MsgControlType.CMSG_CTRL_DECRYPT, ref decryptPara); if (!success) { return(Marshal.GetHRForLastWin32Error().ToCryptographicException()); } return(null); } finally { if (keyAddRefd) { hKey.DangerousRelease(); } } }
private T GetPrivateKey <T>(X509Certificate2 certificate, bool silent, bool preferNCrypt) where T : AsymmetricAlgorithm { if (!certificate.HasPrivateKey) { return(null); } SafeProvOrNCryptKeyHandle handle = GetCertificatePrivateKey( certificate, silent, preferNCrypt, out CryptKeySpec keySpec, out Exception exception); using (handle) { if (handle == null || handle.IsInvalid) { if (exception != null) { throw exception; } return(null); } if (keySpec == CryptKeySpec.CERT_NCRYPT_KEY_SPEC) { using (SafeNCryptKeyHandle keyHandle = new SafeNCryptKeyHandle(handle.DangerousGetHandle(), handle)) { CngKeyHandleOpenOptions options = CngKeyHandleOpenOptions.None; byte clrIsEphemeral = 0; Interop.NCrypt.ErrorCode errorCode = Interop.NCrypt.NCryptGetByteProperty(keyHandle, "CLR IsEphemeral", ref clrIsEphemeral, CngPropertyOptions.CustomProperty); if (errorCode == Interop.NCrypt.ErrorCode.ERROR_SUCCESS && clrIsEphemeral == 1) { options |= CngKeyHandleOpenOptions.EphemeralKey; } using (CngKey cngKey = CngKey.Open(keyHandle, options)) { if (typeof(T) == typeof(RSA)) { return((T)(object)new RSACng(cngKey)); } if (typeof(T) == typeof(ECDsa)) { return((T)(object)new ECDsaCng(cngKey)); } if (typeof(T) == typeof(DSA)) { return((T)(object)new DSACng(cngKey)); } Debug.Fail($"Unknown CNG key type request: {typeof(T).FullName}"); return(null); } } } // The key handle is for CAPI. // Our CAPI types don't allow usage from a handle, so we have a few choices: // 1) Extract the information we need to re-open the key handle. // 2) Re-implement {R|D}SACryptoServiceProvider // 3) PNSE. // 4) Defer to cert.Get{R|D}SAPrivateKey if not silent, throw otherwise. CspParameters cspParams = handle.GetProvParameters(); Debug.Assert((cspParams.Flags & CspProviderFlags.UseExistingKey) != 0); cspParams.KeyNumber = (int)keySpec; if (silent) { cspParams.Flags |= CspProviderFlags.NoPrompt; } if (typeof(T) == typeof(RSA)) { return((T)(object)new RSACryptoServiceProvider(cspParams)); } if (typeof(T) == typeof(DSA)) { return((T)(object)new DSACryptoServiceProvider(cspParams)); } Debug.Fail($"Unknown CAPI key type request: {typeof(T).FullName}"); return(null); } }