Esempio n. 1
0
        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());
            }
        }
Esempio n. 3
0
        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));
                }
            }
        }
Esempio n. 4
0
        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);
        }
Esempio n. 5
0
        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));
                    }
                }));
            }
        }
Esempio n. 6
0
        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());
            }
        }
Esempio n. 7
0
        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();
                }
            }
        }
Esempio n. 9
0
        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);
            }
        }