RSACryptoServiceProvider 与 RSACng
RSACryptoServiceProvider vs RSACng
我有使用 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打开之前创建的密钥?
请注意,我无法使用 提供的答案,因为我无法使用可导出的私钥。最后,密钥应该驻留在 HSM(硬件安全模块)上。
有什么想法吗?
有一个名为 "CngLightup" 的过程,它允许获得对使用 RSACryptoServiceProvider 创建的密钥的 RSACng 引用。它被微软使用,例如,在他们的清单签名实现中:https://github.com/microsoft/referencesource/blob/master/inc/mansign2.cs#L1426
这需要你手上有证书,而不仅仅是关键参考。但是使用手头的密钥创建一个虚拟的自签名证书很容易,然后调用 CngLightup.GetRSAPrivateKey() 瞧,如果你检查 GetType(),你手中就有一个 RSACng 参考。
显然 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));
}
}
}
我有使用 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打开之前创建的密钥?
请注意,我无法使用 提供的答案,因为我无法使用可导出的私钥。最后,密钥应该驻留在 HSM(硬件安全模块)上。
有什么想法吗?
有一个名为 "CngLightup" 的过程,它允许获得对使用 RSACryptoServiceProvider 创建的密钥的 RSACng 引用。它被微软使用,例如,在他们的清单签名实现中:https://github.com/microsoft/referencesource/blob/master/inc/mansign2.cs#L1426
这需要你手上有证书,而不仅仅是关键参考。但是使用手头的密钥创建一个虚拟的自签名证书很容易,然后调用 CngLightup.GetRSAPrivateKey() 瞧,如果你检查 GetType(),你手中就有一个 RSACng 参考。
显然 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));
}
}
}