【问题标题】:RSACryptoServiceProvider vs RSACngRSACryptoServiceProvider 与 RSACng
【发布时间】:2020-04-17 15:59:17
【问题描述】:

我有使用RSACryptoServiceProvider 创建的(不可导出的)密钥。我想使用 RSA-PSS(不是RSACryptoServiceProvider)对数据进行签名。因此,我想获得与RSACng 实例相同的私钥。

我尝试了以下方法:

// Create key with RSACryptoServiceProvider
var keyId = Guid.NewGuid().ToString();
var providerName = "Microsoft Enhanced RSA and AES Cryptographic Provider";
var key = new RSACryptoServiceProvider(2048, new CspParameters(24) {
  ProviderName = providerName,
  KeyContainerName = keyId,
  KeyNumber = (int) KeyNumber.Signature,
  Flags = CspProviderFlags.UseNonExportableKey
});

// Obtain an RSACng reference:
var cngKey = CngKey.Open(keyId, new CngProvider(providerName));
var cngRsaKey = new RSACng(cngKey);

// Sign something using cngRsaKey
[...]

很遗憾,使用WindowsCryptographicException: Keyset does not exist 执行 CngKey.Open 时总是失败。

如何使用 RSACng 打开之前创建的密钥?

请注意,我不能使用https://stackoverflow.com/a/50703729/1400869 提供的答案,因为我不能使用可导出的私钥。最后,密钥应该驻留在 HSM(硬件安全模块)上。

有什么想法吗?

【问题讨论】:

  • 看看这是否有帮助:infinitec.de/post/2010/11/22/…
  • 我不确定以编程方式指定 PIN 与我的问题有何关系。你能详细说明一下吗?
  • 对不起。使用 RSA 处理智能卡问题并混淆了两个帖子。您仍然在证书中设置提供者。智能卡的 PIN 是证书中的密钥。智能卡使用证书。
  • 我这里只处理密钥,没有看到证书。
  • 那么key怎么也有provider名呢?不需要加载证书吗?

标签: c# .net cryptography rsa cng


【解决方案1】:

显然CngKey.Open 不适用于Signature 插槽中的CAPI RSA 密钥(因为它将dwLegacyKeySpec 值硬编码为0)。最简单的解决方法是使用Exchange 插槽;但如果您需要使用签名槽密钥,您可以:

[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
private static extern int NCryptOpenStorageProvider(
    out SafeNCryptProviderHandle phProvider,
    string pszProviderName,
    int dwFlags);

[DllImport("ncrypt.dll", CharSet = CharSet.Unicode)]
private static extern int NCryptOpenKey(
    SafeNCryptProviderHandle hProvider,
    out SafeNCryptKeyHandle phKey,
    string pszKeyName,
    int dwLegacyKeySpec,
    CngKeyOpenOptions dwFlags);

private static void Test61275795()
{
    const string KeyId = "test-982375";

    CspParameters cspParams = new CspParameters(24)
    {
        ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider",
        KeyContainerName = KeyId,
        KeyNumber = (int)KeyNumber.Signature,
        Flags = CspProviderFlags.UseNonExportableKey,
    };

    using (RSACryptoServiceProvider rsaCsp = new RSACryptoServiceProvider(2048, cspParams))
    {
        // Because this is a test, delete the key on Dispose.
        rsaCsp.PersistKeyInCsp = false;

        SafeNCryptKeyHandle hKey;

        int dwError = NCryptOpenStorageProvider(out var hProv, cspParams.ProviderName, 0);

        using (hProv)
        {
            if (dwError != 0)
            {
                throw new CryptographicException(
                    $"{nameof(NCryptOpenStorageProvider)}: 0x{dwError:X8}");
            }

            dwError = NCryptOpenKey(hProv, out hKey, KeyId, cspParams.KeyNumber, 0);

            if (dwError != 0)
            {
                hKey.Dispose();
                throw new CryptographicException($"{nameof(NCryptOpenKey)}: 0x{dwError:X8}");
            }
        }

        using (hKey)
        using (CngKey cngKey = CngKey.Open(hKey, 0))
        using (RSACng rsaCng = new RSACng(cngKey))
        {
            byte[] sig = rsaCng.SignData(
                Array.Empty<byte>(),
                HashAlgorithmName.SHA256,
                RSASignaturePadding.Pss);

            Console.WriteLine(BitConverter.ToString(sig));
        }
    }
}

【讨论】:

  • 谢谢你,很好的回答!
【解决方案2】:

有一个称为“CngLightup”的过程,它允许人们获得对使用 RSACryptoServiceProvider 创建的密钥的 RSACng 引用。 Microsoft 使用它,例如,在清单签名的实现中:https://github.com/microsoft/referencesource/blob/master/inc/mansign2.cs#L1426

这要求您手中有证书,而不仅仅是关键参考。但是很容易使用手头的密钥创建一个虚拟的自签名证书,然后调用 CngLightup.GetRSAPrivateKey() 瞧,如果你检查 GetType(),你手中就有一个 RSACng 参考。

【讨论】:

  • CngLightup 只是框架内部的一个辅助实用程序,用于了解调用(例如)cert.GetRSAPrivateKey() 是否“安全”并执行系统预期的操作;这只是由于 .NET 4.5.2(没有 RSACng)和更高版本(是 RSACng)如何处理共享 GAC。区别在于GetRSAPrivateKey() 背后的管道,我在回答中解决了这个问题。
猜你喜欢
  • 2018-11-14
  • 1970-01-01
  • 2019-12-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-04
相关资源
最近更新 更多