.NET AES解密会破坏前几个字节

时间:2014-01-29 12:03:22

标签: c# encryption cryptography

我正在使用AesCryptoServiceProvider来加密和解密磁盘上的XML文档。 MSDN参考中有一个例子很有帮助。我从给定密码的SHA-256哈希生成AES密钥。它的前半部分被指定为IV,因为我不知道在这里使用什么更好的东西。据我所知,密钥和IV必须与加密和解密相同。

当我解密我的文件时,这就是它的开头:

I���H璧�-����[�="1.0" encoding="utf-8"?>

文件的其余部分非常好。在内容之后甚至没有一些随机填充,正如我所期望的那样。

在文件开头导致此随机垃圾的原因是什么?

以下是更多阅读代码:

using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
    using (SHA256CryptoServiceProvider sha = new SHA256CryptoServiceProvider())
    {
        this.cryptoKey = sha.ComputeHash(Encoding.Unicode.GetBytes(password));
    }
    aes.Key = this.cryptoKey;
    Array.Copy(this.cryptoKey, aes.IV, 16);

    ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);

    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    using (CryptoStream cs = new CryptoStream(fs, decryptor, CryptoStreamMode.Read))
    using (StreamReader sr = new StreamReader(cs))
    {
        string data = sr.ReadToEnd();
        xdoc.LoadXml(data);

        //xdoc.Load(sr);
    }
}

这就是加密代码:

XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.OmitXmlDeclaration = false;

using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
    aes.Key = this.cryptoKey;
    Array.Copy(this.cryptoKey, aes.IV, 16);

    ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);

    using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
    using (CryptoStream cs = new CryptoStream(fs, encryptor, CryptoStreamMode.Write))
    using (StreamWriter sw = new StreamWriter(cs, Encoding.UTF8))
    {
        XmlWriter writer = XmlWriter.Create(sw, xws);
        xdoc.Save(writer);
        writer.Close();
    }
}

3 个答案:

答案 0 :(得分:4)

首先,不要从ad-hoc算法生成密钥材料(是的,当涉及到密钥推导时,SHA256是一种ad-hoc算法)。遵循行业标准并使用可信的基于密码的密钥派生功能。目前的标准为PBKDF-2,另见RFC2898。 .Net托管加密实现是Rfc2898DeriveBytes类。

其次,您必须向我们展示加密代码。在我看来,您使用的示例会附加在加密流开头使用的IV。鉴于不应该从密码导出IV,这是完全合理的。密钥和IV应该来自密码+随机,“随机”必须作为文件的一部分发送。

答案 1 :(得分:2)

按照Remus Rusanu的建议,我已经更改了我的代码,使用Rfc2898DeriveBytes类进行密钥生成,并将使用过的salt和IV数据写入加密文件,这样我就不需要单独传输它了频道(如密码)。

现在这对我有用:

// Setup XML formatting
XmlWriterSettings xws = new XmlWriterSettings();
xws.Encoding = Encoding.UTF8;
xws.Indent = true;
xws.IndentChars = "\t";
xws.OmitXmlDeclaration = false;

// Encrypt document to file
byte[] salt = new byte[8];
new RNGCryptoServiceProvider().GetBytes(salt);
Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(password, salt);

using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
{
    aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);

    using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
    using (CryptoStream cs = new CryptoStream(fs, aes.CreateEncryptor(), CryptoStreamMode.Write))
    using (StreamWriter sw = new StreamWriter(cs, Encoding.UTF8))
    {
        fs.Write(salt, 0, salt.Length);
        fs.Write(aes.IV, 0, aes.IV.Length);

        // Write XmlDocument to the encrypted file
        XmlWriter writer = XmlWriter.Create(sw, xws);
        xdoc.Save(writer);
        writer.Close();
    }
}

// Decrypt the file
using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
    byte[] salt = new byte[8];
    fs.Read(salt, 0, salt.Length);
    Rfc2898DeriveBytes keyGenerator = new Rfc2898DeriveBytes(this.password, salt);

    using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider())
    {
        aes.Key = keyGenerator.GetBytes(aes.KeySize / 8);
        byte[] iv = new byte[aes.BlockSize / 8];
        fs.Read(iv, 0, iv.Length);
        aes.IV = iv;

        using (CryptoStream cs = new CryptoStream(fs, aes.CreateDecryptor(), CryptoStreamMode.Read))
        using (StreamReader sr = new StreamReader(cs))
        {
            // Read stream into new XmlDocument
            xdoc.Load(sr);
        }
    }
}

答案 2 :(得分:1)

对于加密和解密,密钥和IV必须相同才是正确的。在许多情况下,IV被添加到密文之前,需要在解密之前删除。这种情况的症状是在文件真正启动之前的额外垃圾。垃圾是前置IV。

或者,你没有使用相同的IV,在这种情况下,文件的前16个字节是垃圾,你从第17个字节开始得到干净的明文(AES有16个字节的块)。

如果两者都发生了,那么你将得到第一个症状。

你所看到的是第一个。尝试使用传入文件的前16个字节作为IV,其余作为实际的密文。