在使用RSACryptoServiceProvider进行签名之前,请验证私钥保护 [英] Verify Private Key Protection before signing with RSACryptoServiceProvider

查看:503
本文介绍了在使用RSACryptoServiceProvider进行签名之前,请验证私钥保护的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C#中使用RSACryptoServiceProvider签名数据时,我要求确保导入的证书具有强大的密钥保护功能和较高的安全级别,要求用户每次使用密钥签名时都输入密码.这是签名代码的快速简化示例:

When signing data with RSACryptoServiceProvider in C#, I have a requirement to ensure the certificate was imported with strong key protection and a high security level to require the user enters the password every time they sign with the key. Here's a quick simplified sample of the signing code:

X509Store myCurrentUserStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
myCurrentUserStore.Open(OpenFlags.MaxAllowed);
X509Certificate2 currentCertificate = myCurrentUserStore.Certificates[4];

RSACryptoServiceProvider key = new RSACryptoServiceProvider();
key.FromXmlString(currentCertificate.PrivateKey.ToXmlString(true));

byte[] signedData = Encoding.UTF8.GetBytes(originalFileContent);
byte[] signature = key.SignData(signedData, CryptoConfig2.CreateFromName("SHA256CryptoServiceProvider") as HashAlgorithm);    

那么检查证书的安装方式的最佳方法是什么,如果未安装具有高安全性级别的强大私钥保护的证书,我会显示一条错误消息?

So what's the best way to go about checking how the certificate was installed so I can display an error message if it was not installed with strong private key protection with a high security level?

推荐答案

您的代码段中有一些我不理解的东西.

There are a couple things in your snippet that I don't understand.

  1. 为什么要使用MaxAllowed打开.如果您只想阅读,请使用ReadOnly.
  2. 为什么要阅读store.Certificates [4].但这大概只是读取证书"的占位符.
  3. 为什么要导出和重新导入密钥. (特别是因为那将不得不提示,这将挫败您的需要提示"的目标).

对于#3,我假设您只是想拥有一个独特的实例,在这种情况下:好消息! .NET 4.6在X509Certificate2中添加了GetRSAPrivateKey(扩展名)方法,该方法始终返回唯一的实例. (而且,您可能会很高兴知道SignData的新重载,它不鼓励将对象发送到终结器队列:

For #3 I'm assuming you are just looking to have a unique instance, in which case: Good news! .NET 4.6 added a GetRSAPrivateKey (extension) method to X509Certificate2 which always returns a unique instance. (And you might be excited to know about the new overload to SignData which doesn't encourage sending objects to the finalizer queue: https://msdn.microsoft.com/en-us/library/mt132675(v=vs.110).aspx)

无论如何,我在这里写的内容适用于中等(同意)或高级(密码)保护.基于CngKey的方法可以区分高还是中,但是经典的CAPI后备无法分辨是哪一个. (经典的CAPI后备情况只会在晦涩的,没有CNG兼容驱动程序的HSM上发生.)

Anyways, what I wrote here works for medium (consent) or high (password) protection. The CngKey based approach can distinguish medium from high, but the classic CAPI fallback can't tell which is which. (The classic CAPI fallback will only happen with obscure HSMs which don't have a CNG-compatible driver).

private static bool HasProtectedKey(X509Certificate2 cert)
{
    if (!cert.HasPrivateKey)
    {
        return false;
    }

    using (RSA rsa = cert.GetRSAPrivateKey())
    {
        return HasProtectedKey(rsa);
    }
}

private static bool HasProtectedKey(RSA rsa)
{
    RSACng rsaCng = rsa as RSACng;

    if (rsaCng != null)
    {
        return rsaCng.Key.UIPolicy.ProtectionLevel != CngUIProtectionLevels.None;
    }

    RSACryptoServiceProvider rsaCsp = rsa as RSACryptoServiceProvider;

    if (rsaCsp != null)
    {
        CspKeyContainerInfo info = rsaCsp.CspKeyContainerInfo;

        // First, try with the CNG API, it can answer the question directly:
        try
        {
            var openOptions = info.MachineKeyStore
                ? CngKeyOpenOptions.MachineKey
                : CngKeyOpenOptions.UserKey;

            var cngProvider = new CngProvider(info.ProviderName);

            using (CngKey cngKey =
                CngKey.Open(info.KeyContainerName, cngProvider, openOptions))
            {
                return cngKey.UIPolicy.ProtectionLevel != CngUIProtectionLevels.None;
            }
        }
        catch (CryptographicException)
        {
        }

        // Fallback for CSP modules which CNG cannot load:
        try
        {
            CspParameters silentParams = new CspParameters
            {
                KeyContainerName = info.KeyContainerName,
                KeyNumber = (int)info.KeyNumber,
                ProviderType = info.ProviderType,
                ProviderName = info.ProviderName,
                Flags = CspProviderFlags.UseExistingKey | CspProviderFlags.NoPrompt,
            };

            if (info.MachineKeyStore)
            {
                silentParams.Flags |= CspProviderFlags.UseMachineKeyStore;
            }

            using (new RSACryptoServiceProvider(silentParams))
            {
            }

            return false;
        }
        catch (CryptographicException e)
        {
            const int NTE_SILENT_CONTEXT = unchecked((int)0x80090022);

            if (e.HResult == NTE_SILENT_CONTEXT)
            {
                return true;
            }

            throw;
        }
    }

    // Some sort of RSA we don't know about, assume false.
    return false;
}

这篇关于在使用RSACryptoServiceProvider进行签名之前,请验证私钥保护的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆