我正在使用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();
}
}
答案 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,其余作为实际的密文。