【问题标题】:Decryption Exception - length of the data to decrypt is invalid解密异常 - 要解密的数据长度无效
【发布时间】:2020-12-19 06:44:39
【问题描述】:

我正在使用 C# 应用程序。我们有常用的方法将数据存储在文件中。这些方法加密数据并将它们存储在文件系统中。当我们需要数据时,ReadData 方法会解密数据并返回给我纯文本。

如果文本的大小很小,则此代码在正常情况下可以正常工作。但对于下面给出的示例文本,解密代码抛出异常 - 要解密的数据长度无效。

异常发生在行

        // close the CryptoStream
        x_cryptostream.Close();

我尝试了不同的方法,但没有运气。可以请一些帮助。

我为什么要加密已经加密的数据-我只是想使用大型应用程序的常用方法将其存储在文件中。常用方法storedata(key,data) nad readdata(key) 做我无法避免的加密/解密。

   public static byte[] Decrypt(byte[] ciphertext, string Key, string IV)
    {
        byte[] k = Encoding.Default.GetBytes(Key);
        byte[] iv = Encoding.Default.GetBytes(IV);

        // create the encryption algorithm
        SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
        x_alg.Padding = PaddingMode.PKCS7;

        // create an ICryptoTransform that can be used to decrypt data
        ICryptoTransform x_decryptor = x_alg.CreateDecryptor(k, iv);

        // create the memory stream
        MemoryStream x_memory_stream = new MemoryStream();

        // create the CryptoStream that ties together the MemoryStream and the 
        // ICryptostream
        CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
        x_decryptor, CryptoStreamMode.Write);

        // write the ciphertext out to the cryptostream
        x_cryptostream.Write(ciphertext, 0, ciphertext.Length);

        // close the CryptoStream
        x_cryptostream.Close();

        // get the plaintext from the MemoryStream
        byte[] x_plaintext = x_memory_stream.ToArray();

下面是加密方法的代码。

        public static byte[] Encrypt(string strplain, string Key, string IV)
        {
        byte[] k = Encoding.Default.GetBytes(Key);
        byte[] iv = Encoding.Default.GetBytes(IV);

        byte[] plaintext = Encoding.Default.GetBytes(strplain);

        // create the encryption algorithm
        SymmetricAlgorithm x_alg = SymmetricAlgorithm.Create("Rijndael");
        x_alg.Padding = PaddingMode.PKCS7;
        // create an ICryptoTransform that can be used to encrypt data
        ICryptoTransform x_encryptor = x_alg.CreateEncryptor(k, iv);

        // create the memory stream
        MemoryStream x_memory_stream = new MemoryStream();

        // create the CryptoStream that ties together the MemoryStream and
        // the ICryptostream
        CryptoStream x_cryptostream = new CryptoStream(x_memory_stream,
        x_encryptor, CryptoStreamMode.Write);

        // write the plaintext out to the cryptostream
        x_cryptostream.Write(plaintext, 0, plaintext.Length);

        // close the CryptoStream
        x_cryptostream.Close();

        // get the ciphertext from the MemoryStream
        byte[] x_ciphertext = x_memory_stream.ToArray();

        // close memory stream
        x_memory_stream.Close();

        // convert from array to string
        string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 
            0, x_ciphertext.Length);

        x_encryptor.Dispose();

        x_alg.Clear();
        byte[] cipher = Encoding.Default.GetBytes(cipher_Tx);

        return cipher;
    }  

【问题讨论】:

    标签: c# encryption cryptography rijndael


    【解决方案1】:

    你的问题是string cipher_Tx = Encoding.Default.GetString(x_ciphertext, 0, x_ciphertext.Length);

    x_ciphertext 不是文本的有效字节表示,它有许多无法呈现的字符,当您进行 byte[]string 的转换时,您会丢失信息。正确的做法是使用字符串格式,该格式旨在使用Convert.ToBase64String(byte[])Convert.FromBase64String(string) 之类的方式表示二进制数据。

    string cipher_Tx = Convert.ToBase64String(x_ciphertext)
    
    x_encryptor.Dispose();
    
    x_alg.Clear();
    byte[] cipher = Convert.FromBase64String(cipher_Tx)
    

    话虽如此,您的代码还有很多其他“奇怪”的地方,例如您不使用using 语句,而您确实应该使用。此外,完全没有必要将整个转换为字符串并返回,只需返回x_ciphertext。代码也可能存在其他问题(例如 KeyIV 的字符串来自哪里)和许多其他最佳实践(例如您应该生成随机 IV 并将其写入输出和密钥应该使用密钥派生函数而不是直接从用户文本生成),但是在发现字符串转换问题后我停止了检查。

    【讨论】:

    • 感谢您的帮助。非常感谢我修改了我的代码,但这并不能解决我提到的异常。 Key 和 IV 由 ReadData/StoreData 方法传递。这段代码是几年前写的。我相信这与数据块解密算法使用与大小不匹配有关。
    • 嗯,你似乎真的很喜欢使用Encoding.Default.,我敢打赌,几乎每次使用它都是错误的。我会检查程序的其他部分是否存在类似问题。
    • 我查看了与加密解密相关的整个代码,并将 Encoding.Default 的所有行更改为 Convert.FromBase64String/Convert.ToBase64String。我仍然得到要解密的数据长度是无效异常。
    • 没有理由转换为 Base 64 并返回。查看 byte[] x_ciphertext 和 byte[] cipher——它们的值完全相同。只需返回 x_ciphertext - 你已经拥有它。
    【解决方案2】:

    只要用于解密的密钥和 iv 与用于加密的密钥和 iv 匹配,您的上述代码就可以工作。试试这个:

    byte[] test = new byte[1000000];
    for (int i = 0; i < 256; i++)
    {
        test[i] = (byte)i;
    }
    var ciphertext = Encrypt(Encoding.Default.GetString(test), "0000000000000000", "0000000000000000");
    byte[] check = Decrypt(ciphertext, "0000000000000000", "0000000000000000");
    for (int i = 0; i < 256; i++)
    {
        Debug.Assert(check[i] == (byte)i, "round trip");
    }
    

    如您所见,一百万字节的代码可以很好地加密和解密,所以我认为这与数据大小没有任何关系。

    但是,像这样更改 IV:

    byte[] check = Decrypt(ciphertext, "0000000000000000", "000000000000000X"); // note X
    

    并且 Debug.Assert 将触发 - 解密将不匹配。但是,x_cryptostream.Close() 成功。

    接下来,尝试像这样更改密钥:

    byte[] check = Decrypt(ciphertext, "000000000000000X", "0000000000000000"); // note X
    

    现在,x_cryptostream.Close() 将失败并出现 CryptographicException,可能是“填充无效且无法删除。”

    损坏密钥会导致解密失败,x_cryptostream.Close() 失败。

    我认为问题在于您保存并稍后恢复密钥字节。

    顺便说一句:希望您使用密钥的完整二进制范围,而不是仅基于 ASCII 字符,否则您实际上没有强密钥。

    【讨论】:

    • 谢谢。我不小心删除了填充。我认为将编码更改为 Convert.FromBase64String(cipher_Tx) 并添加 x_alg.Padding = PaddingMode.PKCS7 解决了这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-22
    • 1970-01-01
    相关资源
    最近更新 更多