加密器应用中RSA加解密功能异常问题排查求助
问题诊断与修复
错误原因
你遇到的CryptographicException: Key does not exist异常,核心原因是使用BinaryFormatter序列化RSACng导出的私钥参数时,无法完整保留私钥信息。反序列化后导入RSACng时,私钥数据丢失或格式不兼容,导致调用RSA.Decrypt时因缺少私钥抛出错误。
另外代码还存在两个次要问题:
GenerateKey方法声明了size参数但未实际使用,属于冗余代码。- 加密流程中直接将RSA私钥嵌入加密结果,完全失去了非对称加密的安全意义(任何人拿到加密文件都能提取私钥解密),教学场景下需注意这一点。
修复方案
1. 替换序列化方式为XmlSerializer
XmlSerializer对RSAParameters的序列化/反序列化支持更稳定,能完整保留私钥的所有字段信息,解决参数丢失问题。
2. 清理冗余参数
移除GenerateKey方法中未使用的size参数,优化代码结构。
修改后的完整代码
using System; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Xml.Serialization; public static class Crypto { private const int KeySize = 256; private const int IvSize = 128; private const int SaltSize = 64; private const int Iterations = 50000; private static Rfc2898DeriveBytes GenerateKey(string password, byte[] salt) { return new Rfc2898DeriveBytes(password, salt, Iterations); } private static byte[] SerializeRSAParameters(RSAParameters p) { using (var ms = new MemoryStream()) { var serializer = new XmlSerializer(typeof(RSAParameters)); serializer.Serialize(ms, p); return ms.ToArray(); } } private static RSAParameters DeserializeRSAParameters(byte[] p) { using (var ms = new MemoryStream(p)) { var serializer = new XmlSerializer(typeof(RSAParameters)); return (RSAParameters)serializer.Deserialize(ms); } } public static byte[] EncryptData(byte[] data, string password) { var salt = new byte[SaltSize / 8]; using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(salt); } var keyGenerator = GenerateKey(password, salt); var keyBytes = keyGenerator.GetBytes(KeySize / 8); using (var rsa = new RSACng()) { var encryptedKey = rsa.Encrypt(keyBytes, RSAEncryptionPadding.OaepSHA256); var rsaParamsBytes = SerializeRSAParameters(rsa.ExportParameters(true)); Console.WriteLine("RSA params byte length in encryption: " + rsaParamsBytes.Length); var iv = new byte[IvSize / 8]; using (var rng = new RNGCryptoServiceProvider()) { rng.GetBytes(iv); } using (var aes = new AesManaged()) { aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using (var encryptor = aes.CreateEncryptor(keyBytes, iv)) using (var ms = new MemoryStream()) using (var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write)) { cs.Write(data, 0, data.Length); cs.FlushFinalBlock(); var encryptedData = ms.ToArray(); var encryptedKeyLength = BitConverter.GetBytes(encryptedKey.Length); var result = new byte[0] .Concat(salt) .Concat(iv) .Concat(encryptedKeyLength) .Concat(encryptedKey) .Concat(BitConverter.GetBytes(rsaParamsBytes.Length)) .Concat(rsaParamsBytes) .Concat(encryptedData) .ToArray(); Console.WriteLine("Encryption key hash: " + BitConverter.ToString(SHA256.Create().ComputeHash(keyBytes))); return result; } } } } public static byte[] DecryptData(byte[] data, string password) { var salt = data.Take(SaltSize / 8).ToArray(); var iv = data.Skip(SaltSize / 8).Take(IvSize / 8).ToArray(); var encryptedKeyLength = BitConverter.ToInt32(data.Skip((SaltSize + IvSize) / 8).Take(4).ToArray(), 0); var encryptedKey = data.Skip((SaltSize + IvSize) / 8 + 4).Take(encryptedKeyLength).ToArray(); var rsaParamsLength = BitConverter.ToInt32(data.Skip((SaltSize + IvSize) / 8 + 4 + encryptedKeyLength).Take(4).ToArray(), 0); var rsaParamsBytes = data.Skip((SaltSize + IvSize) / 8 + 4 + encryptedKeyLength + 4).Take(rsaParamsLength).ToArray(); Console.WriteLine("RSA params byte length in decryption: " + rsaParamsBytes.Length); using (var rsa = new RSACng()) { rsa.ImportParameters(DeserializeRSAParameters(rsaParamsBytes)); var keyBytes = rsa.Decrypt(encryptedKey, RSAEncryptionPadding.OaepSHA256); Console.WriteLine("Decryption key hash: " + BitConverter.ToString(SHA256.Create().ComputeHash(keyBytes))); var actualData = data.Skip((SaltSize + IvSize) / 8 + 4 + encryptedKeyLength + 4 + rsaParamsLength).ToArray(); using (var aes = new AesManaged()) { aes.Mode = CipherMode.CBC; aes.Padding = PaddingMode.PKCS7; using (var decryptor = aes.CreateDecryptor(keyBytes, iv)) using (var ms = new MemoryStream(actualData)) using (var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Read)) { var decryptedData = new byte[actualData.Length]; var bytesRead = cs.Read(decryptedData, 0, decryptedData.Length); return decryptedData.Take(bytesRead).ToArray(); } } } } }
教学场景优化建议
如果是演示混合加密流程,正确的逻辑应该是:
- 加密方使用接收方的公钥加密AES密钥,而非生成临时RSA密钥并附带私钥。
- 解密方使用自己的私钥解密AES密钥,再解密数据。
这样才能体现非对称加密“公钥加密、私钥解密”的核心安全价值。
内容的提问来源于stack exchange,提问作者Saben
相关产品推荐
相关产品推荐

