我正在课堂上组织一些非常基本的对称加密/解密代码以供将来使用。我能够成功地加密和解密...只有一个小问题。
这是我的代码,它从流中读入并解密到另一个流:
public void Encrypt(Stream input, Stream output) {
byte[] key = Encoding.UTF8.GetBytes(_pw);
byte[] iv = Encoding.UTF8.GetBytes(GenerateInitVector());
RijndaelManaged rm = new RijndaelManaged();
CryptoStream cs = new CryptoStream(
output,
rm.CreateEncryptor(key, iv),
CryptoStreamMode.Write);
int data;
while ((data = input.ReadByte()) != -1)
cs.WriteByte((byte) data);
cs.Close();
}
public void Decrypt(Stream input, Stream output) {
byte[] key = Encoding.UTF8.GetBytes(_pw);
byte[] iv = Encoding.UTF8.GetBytes(GenerateInitVector());
RijndaelManaged rm = new RijndaelManaged();
CryptoStream cs = new CryptoStream(
input,
rm.CreateDecryptor(key, iv),
CryptoStreamMode.Read);
int data;
while ((data = cs.ReadByte()) != -1)
output.WriteByte((byte) data);
cs.Close();
}
有关您的信息,方法GenerateInitVector()始终返回相同的值。
这是我的测试文件。
hello
world
this
is
a
word
list
当我尝试从FileStream进行/解密到FileStream时,一切正常,使用这些方法:
public void Encrypt(string inputPath, string outputPath) {
FileStream input = new FileStream(inputPath, FileMode.Open);
FileStream output = new FileStream(outputPath, FileMode.Create);
Encrypt(input, output);
output.Close();
input.Close();
}
public void Decrypt(string inputPath, string outputPath) {
FileStream input = new FileStream(inputPath, FileMode.Open);
FileStream output = new FileStream(outputPath, FileMode.Create);
Decrypt(input, output);
output.Close();
input.Close();
}
当我尝试将文件解密为MemoryStream然后将bytes-converted-to-chars数组加载到StringBuilder中,最后将其作为字符串打印到控制台时,我进入控制台:
?hello
world
this
is
a
word
list
我的所有文字前面都有一个额外的问号。这是我的解密方法:
public StringBuilder Decrypt(string inputPath) {
FileStream input = new FileStream(inputPath, FileMode.Open);
byte[] buffer = new byte[4096];
MemoryStream output = new MemoryStream(buffer);
Decrypt(input, output);
StringBuilder sb = new StringBuilder(4096);
sb.Append(Encoding.UTF8.GetChars(buffer, 0, (int) output.Position));
input.Close();
output.Close();
return sb;
}
我在这里读过类似于BOM和C#的东西,悄悄写下未知的东西作为'?'字符:
Result of RSA encryption/decryption has 3 question marks
因此我更加确定我使用的是UTF-8编码。不过,我看到这个额外的问号。为了完整起见,我还编写了这个方法来将StringBuilder中的内容加密到文件:
public void Encrypt(string outputPath, StringBuilder builder) {
char[] buffer = new char[builder.Length];
builder.CopyTo(0, buffer, 0, builder.Length);
byte[] buf = Encoding.UTF8.GetBytes(buffer);
MemoryStream input = new MemoryStream(buf);
FileStream output = new FileStream(outputPath, FileMode.Create);
Encrypt(input, output);
output.Close();
input.Close();
}
然后我通过以下方式进行交叉检查:
var builder = new StringBuilder();
builder.Append("Hello world.");
Encrypt("test.txt.enc", builder);
Decrypt("test.txt.enc", "test.txt");
builder = Decrypt("test.txt.enc");
Console.WriteLine(builder.ToString());
对于文件test.txt,一切都很好。奇怪的是,对于打印在控制台上的文字,我在前面得到了 NO 额外的问号:
Hello world.
整个过程有什么问题?
答案 0 :(得分:6)
问号是UTF8的BOM(字节顺序标记)是0xef 0xbb 0xbf 这些字节写在以UTF8编码的文件的开头。
因为FileStream将文件作为字节读取并且没有将其作为UTF8文本文件进行解释,所以BOM包含在您的加密中,因此如果您将其解密并将其保存到文件,它在TextEditor中看起来很好但是如果您将其转储到控制台BOM中还打印因为控制台不知道它是某种控制序列/标记
编辑: 以下是获取没有BOM的字符串的解决方案。
public static string Decrypt(string inputPath)
{
FileStream input = new FileStream(inputPath, FileMode.Open);
MemoryStream output = new MemoryStream();
Decrypt(input, output);
StreamReader reader = new StreamReader(output, new UTF8Encoding()); //Read with encoding
output.Seek(0, SeekOrigin.Begin); //Set stream Position to beginning
string result = reader.ReadToEnd(); //read to string
reader.Close();
input.Close();
output.Close();
return result;
}
答案 1 :(得分:3)
一些问题:
密钥和IV是固定长度的二进制序列,可以包含任意字节,因此UTF-8不能正确。
从名为pw
的变量中复制密钥,可能是“密码”的缩写,表示存在相关的混淆。密码不是密钥。您应该使用密钥派生函数,最好是专门用于密码散列的函数,如PBKDF2或scrypt。
使用固定的IV错过了IV的全部要点。您需要为每条消息使用新的随机值。这不是秘密,所以只需将它放在密文之前。
Stream.CopyTo
在两个流之间复制数据。