C# .NET AES解密会中断前几个字节

C# .NET AES解密会中断前几个字节,c#,encryption,cryptography,C#,Encryption,Cryptography,我正在使用AESCryptServiceProvider对磁盘上的XML文档进行加密和解密。MSDN参考资料中有一个很有帮助的例子。我从给定密码的SHA-256散列生成AES密钥。它的前半部分被指定为IV,因为我不知道有什么更好的东西可以在这里使用。据我所知,密钥和IV必须是相同的加密和解密 当我解密我的文件时,它的开头是这样的: I���H璧�-����[�="1.0" encoding="utf-8"?> 文件的其余部分非常好。在内容后面甚至没有一些随机填充,这可能是我所期望的 是什

我正在使用AESCryptServiceProvider对磁盘上的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();
    }
}

首先,不要从特殊算法生成密钥材料(是的,当涉及到密钥派生时,SHA256是一种特殊算法)。遵循行业标准并使用基于可信密码的密钥派生函数。现行标准为,另见。Net托管加密实现是类


第二,你必须给我们看加密代码。在我看来,您使用的示例附加了加密流开头使用的IV。这很有道理,因为IV不应该从密码派生。密钥和IV应该从password+random派生,并且“random”必须作为文件的一部分发送。

您认为密钥和IV需要相同才能进行加密和解密,这是正确的。在许多情况下,IV是在密码文本前加上的,需要在解密之前删除。这种情况的症状是在文件真正开始之前有额外的垃圾。垃圾是预先准备好的IV

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

如果这两种情况都发生了,那么您将出现第一个症状


你所拥有的似乎是第一个。尝试使用传入文件的前16个字节作为您的IV,其余的作为实际的密码文本。

根据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);
        }
    }
}

好的,我会研究RFC2898的问题。我只是想有一个简单的解决方案,使用密码字符串而不是庞大的字节数组。此外,我在我的问题中添加了加密代码。嗯,你有这样的例子吗?我无法让它工作。当我将salt和IV写入文件并使用它进行解密时,我仍然会得到随机字节而不是XML头。“密钥和IV应该来自密码+随机”>我知道你的意思,但读起来有点混乱。也许相反,“密钥应该来自密码,IV应该完全随机。”,还是我误解了你的意思?好的,明白了。从文件中读取的IV无效。我更改了它,现在文件解密完全正确。我要更新上面的代码。我仍然不明白为什么使用SHA-256作为密钥,一半作为IV不起作用。它可能较弱,但毕竟只是数字和数学。@LonelyPixel
我不明白为什么使用SHA-256作为密钥,一半作为IV不应该起作用
:因为你不是密码学家。我也不是。这就是为什么我只是遵循行业惯例。内容中没有预先添加垃圾,而是替换了内容的第一块。我想每个人都知道正确的XML声明是如何实现的在线看起来像…感谢您指出我的错误。如果这是第二个症状,那么请查看您正在使用的IV,并确保它与用于加密的IV是逐字节相同的。您的密钥很好,因为其余的消息都已正确解码。请专注于正确使用IV。感谢您以LonelyPixel(+1)。请不要忘记接受Remus的回答。+1并感谢您发布解决方案。但是您不应该写出IV,只应该写出salt。
IV
应该由
keyGenerator
生成。加密机和解密机都会生成相同的IV。Remus,如果我不将IV保存到文件中,我应该从哪里获取IV?keyGenerator没有GetIV方法之类的。我知道了,我可以在读取和写入文件时使用keyGenerator中的更多字节并将它们分配给aes.IV。