簡體   English   中英

C#加密和解密IV問題

[英]C# encryption and decryption IV issue

我的代碼執行加密,但不執行解密。 我希望它生成隨機IV,因此進行了一些更改,但現在不進行解密。 我想我搞砸了IV。 似乎它無法正確解密。

IV未加到加密文件的前綴,或者解密方法無法找到IV。 我不明白該怎么解決。 例如,生成文件時,我加密了具有“ hello world”的文本文件,加密后產生了一些亂碼。 解密后,它產生一個空的文本文件。

加密方式:

private const ushort ITERATIONS = 1300;
private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

private static byte[] CreateKey(string password, int keySize)
{
    DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, SALT, ITERATIONS);
    return derivedKey.GetBytes(keySize >> 3);
}


public static void EncryptFile(string file, string password)
{

    // First we are going to open the file streams 
    FileStream fsIn = new FileStream(file, FileMode.Open, FileAccess.Read);
    FileStream fsOut = new FileStream(file+"enc", FileMode.OpenOrCreate, FileAccess.Write);

    // Then we are going to derive a Key and an IV from the
    // Password and create an algorithm 
    byte[] passwordBytes = Encoding.UTF8.GetBytes(password);


    // passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    RijndaelManaged AES = new RijndaelManaged();
    AES.KeySize = AES.LegalKeySizes[0].MaxSize;
    AES.BlockSize = AES.LegalBlockSizes[0].MaxSize;
    AES.Padding = PaddingMode.Zeros;

    AES.GenerateIV();
    AES.Key = CreateKey(password, AES.KeySize);
    AES.Mode = CipherMode.CBC;
    using (MemoryStream memStream = new MemoryStream(file.Length)) 
        memStream.Write(AES.IV, 0, 16);

    CryptoStream cs = new CryptoStream(fsOut, AES.CreateEncryptor(), CryptoStreamMode.Write);

    int bufferLen = 4096;
    byte[] buffer = new byte[bufferLen];
    int bytesRead;

    do
    {
        // read a chunk of data from the input file 
        bytesRead = fsIn.Read(buffer, 0, bufferLen);

        // encrypt it 
        cs.Write(buffer, 0, bytesRead);
    } while (bytesRead != 0);

    // close everything 

    // this will also close the unrelying fsOut stream
    cs.Close();
    fsIn.Close();

}

解密方法:

private const ushort ITERATIONS = 1300;
private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

private static byte[] CreateKey(string password, int keySize)
{
    DeriveBytes derivedKey = new Rfc2898DeriveBytes(password, SALT, ITERATIONS);
    return derivedKey.GetBytes(keySize >> 3);
}


public static void DecryptFile(string fileIn, string Password)
{

    // First we are going to open the file streams 
    FileStream fsIn = new FileStream(fileIn,FileMode.Open, FileAccess.Read);

    string extension = System.IO.Path.GetExtension(fileIn);
    string result = fileIn.Substring(0, fileIn.Length - extension.Length);

    FileStream fsOut = new FileStream(result,FileMode.OpenOrCreate, FileAccess.Write);



  //  passwordBytes = SHA256.Create().ComputeHash(passwordBytes);

    RijndaelManaged AES = new RijndaelManaged();
    AES.KeySize = AES.LegalKeySizes[0].MaxSize;
    AES.BlockSize = AES.LegalBlockSizes[0].MaxSize;
    AES.Padding = PaddingMode.Zeros;
    byte[] iv = new byte[16];
    fsIn.Read(iv, 0, 16);
    AES.IV=iv;

    AES.Key = CreateKey(Password, AES.KeySize);
    AES.Mode = CipherMode.CBC;


    // Now create a crypto stream through which we are going
    // to be pumping data. 
    // Our fileOut is going to be receiving the Decrypted bytes. 
    CryptoStream cs = new CryptoStream(fsOut,
        AES.CreateDecryptor(), CryptoStreamMode.Write);

    // Now will will initialize a buffer and will be 
    // processing the input file in chunks. 
    // This is done to avoid reading the whole file (which can be
    // huge) into memory. 
    int bufferLen = 4096;
    byte[] buffer = new byte[bufferLen];
    int bytesRead;

    do
    {
        // read a chunk of data from the input file 
        bytesRead = fsIn.Read(buffer, 0, bufferLen);

        // Decrypt it 
        cs.Write(buffer, 0, bytesRead);

    } while (bytesRead != 0);

    // close everything 
    cs.Close(); // this will also close the unrelying fsOut stream 
    fsIn.Close();

}

您必須指定128位(16字節)的塊大小才能使加密為AES,請參見以下選項:

通常,iv與塊大小相同。 您提供的是16字節的iv,Rijndael的最大塊大小為32字節,因此,加密例程很有可能在iv之后使用額外的16字節的垃圾字節。

存在一些問題:類是Rijndael,AES是Rijndael的子集,因此Rijndael的允許參數對於AES可能是不允許的。

  1. LegalBlockSizes[0].MaxSize將返回Rijndael最大塊大小為256位,但是AES的固定塊大小為128位。 因此,您實際上並沒有使用AES。 您必須指定一個128位(16字節)的塊大小。

  2. 如果要加密的數據的最后一個字節為0x00字節,則PaddingMode.Zeros將不起作用。 通常使用PKCS#7(通常為PKCS#5),PHP mcrypt除外。 -感謝ArtjomB。

根據ArtjomB的提示。
如果我是對的,那么有兩個選擇,請選擇其中一個:

1:根據AES的要求將塊大小更改為16字節:
更改為加密和解密:

AES.BlockSize = 128;

2:使用32字節的iv(請注意,這不會產生AES加密):
更改為加密:

memStream.Write(AES.IV, 0, 32);  

更改為解密:

byte[] iv = new byte[32];
fsIn.Read(iv, 0, 32

以zaph的答案為基礎...

您需要將IV寫入文件,而不要寫入從未使用過的一些臨時MemoryStream:

fsOut.Write(AES.IV, 0, 32);

using (MemoryStream memStream = new MemoryStream(file.Length))完全刪除該行。

閱讀注釋時,這似乎是一個問題:

例如,生成文件時,我加密了具有“ hello world”的文本文件,加密后產生了一些亂碼。 解密后產生一個空的文本文件

這意味着實際的密文為空,沒有要解密的內容。 之所以發生這種情況,是因為IV並未真正寫入密文文件,因此解密方法認為其中存在的單個塊實際上就是IV。

不要忘記在解密過程中從密文文件中讀取完整的IV:

byte[] iv = new byte[32];
fsIn.Read(iv, 0, 32)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM