C#加密和解密IV问题

时间:2015-12-16 18:57:16

标签: c# encryption file-io cryptography

我的代码执行加密但不执行解密。我想让它生成随机IV,所以做了一些改动,但现在它没有解密。我想我搞砸了IV。它似乎不能正确解密。

IV没有以加密文件为前缀,或者解密方法无法找到IV。我无法理解如何解决它。例如,我生成的文件加密了一个文本文件,该文件有“hello world”,加密后产生了一些乱码。解密后,它产生了一个空文本文件。

加密方法:

-module(g).
-compile(export_all).
-define(height, 500).
-define(width, 500).  
-include_lib("wx/include/wx.hrl").
-define(EXIT,?wxID_EXIT).

init() ->
    start().

start() ->
    Wx = wx:new(),
    Frame = wxFrame:new(Wx, -1, "Line", [{size, {?height, ?width}}]),   
    setup(Frame),
    wxFrame:show(Frame),
    loop(Frame).

setup(Frame) ->
    menuBar(Frame),
    wxFrame:connect(Frame, close_window).

menuBar(Frame) ->
    MenuBar = wxMenuBar:new(),
    File = wxMenu:new(),
    wxMenuBar:append(MenuBar,File,"&Fichier"),
    wxFrame:setMenuBar(Frame,MenuBar),  
    Quit = wxMenuItem:new ([{id,400},{text, "&Quit"}]),
    wxMenu:append (File, Quit).

loop(Frame) ->
    receive
        #wx{event=#wxCommand{type=close_window}} ->
            io:format("quit icon"),
            wxWindow:close(Frame,[]);       

        #wx{id=?EXIT, event=#wxCommand{type=command_menu_selected}} ->
            io:format("quit file menu"),
            wxWindow:close(Frame,[])
    end.

解密方法:

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();

}

2 个答案:

答案 0 :(得分:3)

您必须将加密的块大小指定为128位(16字节)为AES,请参阅以下选项:

通常,iv与块大小相同。您正在提供一个16字节的iv,Rijndael最大块大小为32字节,因此加密例程很可能在iv之后使用额外的16字节垃圾字节。

存在一些问题:该类是Rijndael,AES是Rijndael的子集,因此可能不允许使用Rijndael的允许参数。

  1. LegalBlockSizes[0].MaxSize将返回最大Rijndael块大小为256位,但AES的固定块大小为128位。因此,您实际上并没有使用AES。您必须指定128位(16字节)的块大小。

  2. 如果要加密的数据的最后一个字节是PaddingMode.Zeros字节,则
  3. 0x00将不起作用。通常使用PKCS#7(基本上是PKCS#5),PHP mcrypt除外。 - 感谢ArtjomB。

  4. 根据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
    

答案 1 :(得分:1)

以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)