我正在将一些 .NET Framework 库切换到 .NET Standard。我的一个库使用本地计算机上的证书存储来处理 JSON Web token (JWT)。该库正在使用 RSACryptoServiceProvider
,看起来是 not recommended不再了。
因此,我改用 GetPublicKey()
和 GetPrivateKey()
扩展方法,但我遇到了私钥问题。每当我对收到的私钥的 RSA 实例调用 Decrypt
时:
{Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Access denied at System.Security.Cryptography.RSACng.EncryptOrDecrypt(SafeNCryptKeyHandle key, Byte[] input, AsymmetricPaddingMode paddingMode, Void* paddingInfo, EncryptOrDecryptAction encryptOrDecrypt) at System.Security.Cryptography.RSACng.EncryptOrDecrypt(Byte[] data, RSAEncryptionPadding padding, EncryptOrDecryptAction encryptOrDecrypt) at System.Security.Cryptography.RSACng.Decrypt(Byte[] data, RSAEncryptionPadding padding)
以下是导致异常的代码的简短示例:
public X509Certificate2 GetCert() {
using (var certStore = new X509Store(StoreName.My, StoreLocation.LocalMachine)) {
certStore.Open(OpenFlags.ReadOnly);
var certMatches = certStore.Certificates.Find(X509FindType.FindByThumbprint, CertificateThumbprint, false);
return certMatches[0];
}
}
var cert = GetCert();
var publicKey = cert.GetRSAPublicKey();
var encryptedBytes = publicKey.Encrypt(bytes, System.Security.Cryptography.RSAEncryptionPadding.OaepSHA256);
var privateKey = cert.GetRSAPrivateKey();
// Exception on this line. :(
var decryptedBytes = privateKey.Decrypt(encryptedBytes, System.Security.Cryptography.RSAEncryptionPadding.OaepSHA256);
使用RSACryptoServiceProvider
可以使用相同的代码。我已验证用户有权访问商店中证书的私钥。
是什么原因导致此访问被拒绝异常?
最佳答案
问题似乎是,当创建(或导入)私钥时,它被标记为仅签名 key 。使用 CNG key ,可以(并且在本例中已经)通过检查 KeyUsages
来验证这一点。 CngKey
的属性(property)对象(((RSACng)privateKey).Key.KeyUsages
)。
在继续之前,请查看 RSACryptoServiceProvider
您的 key 的版本。 rsaCsp.CspParameters.KeyNumber
这就是我们真正寻找的。p>
switch (rsaCsp.CspParameters.KeyNumber)
{
case 0:
You're on the CAPI-to-CNG bridge, new to Windows 10.
Keep going, this is the answer I answered.
break;
case 1:
This is a CAPI AT_KEYEXCHANGE key.
Things should just work...
break;
case 2:
This is a CAPI AT_SIGNATURE key, I don't understand why CAPI allowed decryption.
A different answer is required.
break;
default:
throw new ArgumentOutOfRangeException();
}
从技术上讲, key “最终确定”后(CNG 术语,而不是 .NET 术语), key 用法就无法更改。但如果 key 是可导出的,您可以使用类似的方法解决问题
private static CngKey ResetKeyUsage(CngKey key)
{
CngKeyCreationParameters keyParameters = new CngKeyCreationParameters
{
ExportPolicy = key.ExportPolicy,
KeyCreationOptions = CngKeyCreationOptions.OverwriteExistingKey,
};
if (key.IsMachineKey)
{
keyParameters.Parameters.Add(
key.GetProperty("Security Descr", (CngPropertyOptions)4));
keyParameters.KeyCreationOptions |= CngKeyCreationOptions.MachineKey;
}
CngKeyBlobFormat rsaPrivateBlob = new CngKeyBlobFormat("RSAPRIVATEBLOB");
keyParameters.Parameters.Add(
new CngProperty(
rsaPrivateBlob.Format,
key.Export(rsaPrivateBlob),
CngPropertyOptions.Persist));
CngAlgorithm alg = key.Algorithm;
string name = key.KeyName;
CngKey newKey = CngKey.Create(alg, name, keyParameters);
key.Dispose();
return newKey;
}
请注意,这实际上应该是一次性操作,因为它无疑会对任何打开的 key 句柄造成不良影响。
如果 ExportPolicy 表示它是可导出的,但不是 PlaintextExportable,则涉及更复杂的繁琐操作(并且通过直接 P/Invoking 可能会变得更容易完成)。
关于encryption - RSA解密抛出 'Access denied'异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51993985/