You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

加密器应用中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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:35:11