使用Rijndael加密/解密文件

7

我需要传输XML文件,这些文件需要加密。我已经找到了一些例子,认为我接近成功,但是当我解密文件时,最终会出现一些无用的字符。有一些关于此问题的帖子,但我没有看到任何确切的帮助。以下是加密和解密代码。

private void EncryptFile(string inputFile, string outputFile, string key) {
    try {
        byte[] keyBytes;
        keyBytes = Encoding.Unicode.GetBytes(key);

        Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(key, keyBytes);

        RijndaelManaged rijndaelCSP = new RijndaelManaged();
        rijndaelCSP.Key = derivedKey.GetBytes(rijndaelCSP.KeySize / 8);
        rijndaelCSP.IV = derivedKey.GetBytes(rijndaelCSP.BlockSize / 8);

        ICryptoTransform encryptor = rijndaelCSP.CreateEncryptor();

        FileStream inputFileStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read);

        byte[] inputFileData = new byte[(int)inputFileStream.Length];
        inputFileStream.Read(inputFileData, 0, (int)inputFileStream.Length);

        FileStream outputFileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write);

        CryptoStream encryptStream = new CryptoStream(outputFileStream, encryptor, CryptoStreamMode.Write);
        encryptStream.Write(inputFileData, 0, (int)inputFileStream.Length);
        encryptStream.FlushFinalBlock();

        rijndaelCSP.Clear();
        encryptStream.Close();
        inputFileStream.Close();
        outputFileStream.Close();
    }
    catch (Exception ex) {
        MessageBox.Show(ex.Message, "Encryption Failed!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    MessageBox.Show("File Encryption Complete!");

}

private void DecryptFile(string inputFile, string outputFile, string key) {
    try {
        byte[] keyBytes = Encoding.Unicode.GetBytes(key);

        Rfc2898DeriveBytes derivedKey = new Rfc2898DeriveBytes(key, keyBytes);

        RijndaelManaged rijndaelCSP = new RijndaelManaged();
        rijndaelCSP.Key = derivedKey.GetBytes(rijndaelCSP.KeySize / 8);
        rijndaelCSP.IV = derivedKey.GetBytes(rijndaelCSP.BlockSize / 8);
        ICryptoTransform decryptor = rijndaelCSP.CreateDecryptor();

        FileStream inputFileStream = new FileStream(inputFile, FileMode.Open, FileAccess.Read);

        CryptoStream decryptStream = new CryptoStream(inputFileStream, decryptor, CryptoStreamMode.Read);

        byte[] inputFileData = new byte[(int)inputFileStream.Length];
        decryptStream.Read(inputFileData, 0, (int)inputFileStream.Length);

        FileStream outputFileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
        outputFileStream.Write(inputFileData, 0, inputFileData.Length);
        outputFileStream.Flush();

        rijndaelCSP.Clear();

        decryptStream.Close();
        inputFileStream.Close();
        outputFileStream.Close();
    }
    catch (Exception ex) {
        MessageBox.Show(ex.Message, "Decryption Failed!", MessageBoxButtons.OK, MessageBoxIcon.Error);
        return;
    }

    MessageBox.Show("File Decryption Complete!");
}

我最终得到的是:
<?xml version="1.0" encoding="UTF-8"?>
<transaction>
  <header>
    <qOrderNumber></qOrderNumber>
    <qRequestDate></qRequestDate>
    <testOrder></testOrder>
    <qCustomerNumber></qCustomerNumber>
    <transactionStatus></transactionStatus>
  </header>
  <lines>
    <line>
      <productID></productID>
      <serialNumber></serialNumber>
    </line>
    <line> 
      <productID></productID>
      <serialNumber></serialNumber>
    </line>
  </lines>
</transaction>NULNULNULNULNULNUL
2个回答

11

解密时,需要注意CryptoStream.Read调用的返回值。它会告诉您byte数组中解密数据的长度(由于填充,通常不会与加密数据的长度匹配)。在解密函数中尝试使用以下代码:

int decrypt_length = decryptStream.Read(inputFileData, 0, (int)inputFileStream.Length);
FileStream outputFileStream = new FileStream(outputFile, FileMode.Create, FileAccess.Write);
outputFileStream.Write(inputFileData, 0, decrypt_length);

3
RijndaelManaged 对象上,将 Padding 属性设置为 PaddingMode.ANSIX923PaddingMode.ISO10126
这些空字节被添加以填充最后一个加密块。默认情况下,它使用零进行填充,这意味着无法指示实际数据的长度。其他填充模式包括在最后一个字节中包含长度,以便在解密后可以删除填充。
在加密和解密例程中都将填充属性设置为相同的值。
 rijndaelCSP.Padding = PaddingMode.ANSIX923;

如果解密流知道预期结果,它将自动删除填充,因此不需要进行进一步更改。

更新

从您的代码中可以看出,您写入输出文件的字节数与从输入文件中读取的字节数相等。

byte[] inputFileData = new byte[(int)inputFileStream.Length];
decryptStream.Read(inputFileData, 0, (int)inputFileStream.Length);

解密过程不会完全填满inputFileData数组,因为输入中存在填充。
输出流将写出整个缓冲区的长度,即使它没有被完全填满。
outputFileStream.Write(inputFileData, 0, inputFileData.Length);

这是您的空值来源。

您可能希望更改加密和解密方式,使其不再使用固定长度的缓冲区。或者,您可以在开始时存储加密数据的长度,并仅写入与该长度相对应的字节数。


+1 我以前也遇到过这个问题。我相信这就是我用来解决它的方案。 - Rex Morgan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接