从RSACryptoServiceProvider迁移至RSACng:旧加密数据解密问题
我来帮你梳理下这个问题,其实核心是填充算法不匹配和字节序处理细节没搞对,咱们一步步解决:
问题根源拆解
填充算法不匹配(最关键)
RSACryptoServiceProvider调用Encrypt(plainBytes, true)时,默认使用的是OAEP填充 + SHA-1哈希算法,而你在RSACng解密时错误地用了RSAEncryptionPadding.OaepSHA512,这是抛出"参数不正确"异常的核心原因。字节序处理逻辑混乱
CAPI(RSACryptoServiceProvider)的RSA密文是小端字节序(低位字节在前),CNG(RSACng)则用大端字节序(高位字节在前)。你的加密代码已经对密文做了反转转大端,但解密时的字节转换环节可能没对应上。密钥导入冗余且易出错
你通过RSACryptoServiceProvider导出密钥再导入RSACng的方式完全没必要,直接用CNG API打开密钥容器更可靠,还能避免导出导入的潜在问题。
修正后的解决方案
步骤1:统一填充算法
将RSACng解密时的填充改为RSAEncryptionPadding.OaepSHA1,和RSACryptoServiceProvider的默认OAEP填充完全匹配。
步骤2:修正字节序与字符串转换
你的加密代码已经把CAPI的小端密文反转成大端,再转成十六进制字符串。解密时只需把十六进制字符串转回字节数组(这个数组已经是CNG需要的大端格式),不需要再次反转。
步骤3:简化密钥导入逻辑
直接用CNG API打开密钥容器,跳过RSACryptoServiceProvider中转环节。
修正后的代码示例
解密代码(核心修正)
private static byte[] DecryptWithRSACng(string cipherHexText) { // 将加密时生成的十六进制字符串转回字节数组(已为大端格式) byte[] cipherBytes = HexStringToByteArray(cipherHexText); // 直接通过CNG打开机器密钥库中的密钥容器 CngKeyOpenParameters openParams = new CngKeyOpenParameters(regValue.ToString()) { KeyStoreProviderName = CngProvider.MicrosoftSoftwareKeyStorageProvider.Provider, Flags = CngKeyOpenOptions.MachineKeyStore, KeyType = CngKeyType.Rsa }; using (CngKey cngKey = CngKey.Open(openParams)) using (RSACng rsacng = new RSACng(cngKey)) { // 使用与CAPI一致的OAEP SHA-1填充 return rsacng.Decrypt(cipherBytes, RSAEncryptionPadding.OaepSHA1); } } // 辅助方法:十六进制字符串转字节数组(处理小写格式) private static byte[] HexStringToByteArray(string hex) { int byteCount = hex.Length / 2; byte[] result = new byte[byteCount]; for (int i = 0; i < byteCount; i++) { result[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16); } return result; }
加密代码的验证(无需修改)
你的加密代码中反转字节数组的逻辑是正确的,把CAPI的小端密文转成了CNG兼容的大端格式:
// 正确:将CAPI小端密文反转成大端,再转十六进制字符串 Array.Reverse(encBlockData); cypherText = BitConverter.ToString(encBlockData).Replace("-", "").ToLower();
常见错误排查
- 若你曾经通过
SetOaepParameters修改过CAPI的OAEP哈希算法,需要同步修改RSACng的填充参数。 - 确保运行代码的用户有权限访问机器密钥库中的密钥容器,否则会出现密钥找不到或访问被拒绝的错误。
- 检查十六进制字符串转字节数组的逻辑,避免因大小写、字符遗漏导致的字节数组错误。
内容的提问来源于stack exchange,提问作者sagar yadwad

