簡體   English   中英

為什么在加密字符串時得到一個空結果,即使我刷新了輸入 stream?

[英]Why do I get an empty result when encrypting a string, even though I flushed the input stream?

我想通過 RijndaelManaged 加密一些字符串並獲得加密結果。 我想用 MemoryStream 來做。 但我得到了空字符串。 如果我使用Rijndael class 而不是RijndaelManaged ,我會遇到同樣的問題。 我做錯了什么?

static string EncodeString(string text, byte[] key, byte[] iv) {
    RijndaelManaged alg = new RijndaelManaged();
    var encryptor = alg.CreateEncryptor(key, iv);
    string encodedString = null;
    using (var s = new MemoryStream())
    using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
    using (var sw = new StreamWriter(cs)) {
        // encrypt the string
        sw.Write(text);
        sw.Flush();
        cs.Flush();
        s.Flush();
        Console.WriteLine($"Stream position: {s.Position}"); // Oops... It is 0 still. Why?
        // get encrypted string
        var sr = new StreamReader(s);
        s.Position = 0;
        encodedString = sr.ReadToEnd(); // I get empty string here
    }
    return encodedString;
}

然后我使用這個方法:

    RijndaelManaged alg = new RijndaelManaged();
    alg.GenerateKey();
    alg.GenerateIV();
    var encodedString = EncodeString("Hello, Dev!", alg.Key, alg.IV); // I get empty string. Why?

您在這里有兩個問題:


問題 1:您嘗試在結果准備好之前讀取結果。 您需要先關閉 StreamWriter:

using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
using (var sw = new StreamWriter(cs)) {
    // encrypt the string
    sw.Write(text);
    Console.WriteLine(s.ToArray().Length); // prints 0
    sw.Close();
    Console.WriteLine(s.ToArray().Length); // prints 16
    ...
}

但我為什么需要這個? 你沒有在我的代碼中看到所有這些Flush語句嗎? 是的,但是 Rijndael 是一個塊 cypher。 它只能在讀取完整塊(或者您告訴它這是最后的部分塊)后加密一個塊。 Flush允許將更多數據寫入 stream,因此加密器無法確定塊是否完整。

您可以通過明確告訴加密 stream 您已完成發送輸入來解決此問題。 參考實現通過使用嵌套的 using 語句關閉 StreamWriter(以及 CryptoStream)來實現這一點。 一旦 CryptoStream 關閉,它就會刷新最后一個塊。

using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
{ 
    using (var sw = new StreamWriter(cs))
    {
        // encrypt the string
        sw.Write(text);
    }
    Console.WriteLine(s.ToArray().Length); // prints 16
    ...
}

或者,正如 Jimi 在評論中提到的,您可以顯式調用FlushFinalBlock 此外,您可以通過將基本字符串顯式轉換為字節數組來跳過 StreamWriter:

using (var s = new MemoryStream())
using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
{
    cs.Write(Encoding.UTF8.GetBytes(text));
    cs.FlushFinalBlock();
    Console.WriteLine(s.ToArray().Length); // prints 16
    ...
}

或者,正如 V.Lorz 在評論中提到的那樣,您可以將 CryptoStream 配置為隱式調用 FlushFinalBlock:

using (var s = new MemoryStream())
{
    using (var cs = new CryptoStream(s, encryptor, CryptoStreamMode.Write))
    {
        cs.Write(Encoding.UTF8.GetBytes(text));
    }
    Console.WriteLine(s.ToArray().Length); // prints 16
    ...
}

問題 2:您嘗試將結果讀取為字符串。 加密不適用於字符串,它適用於字節 arrays。 因此,嘗試將結果讀取為 UTF-8 字符串將導致垃圾。

相反,例如,您可以使用生成的字節數組的 Base64 表示:

return Convert.ToBase64String(s.ToArray());

以下是應用了所有這些修復程序的代碼的工作小提琴:

  1. 使用 StreamWriter: https://dotnetfiddle.net/8kGI4N
  2. 沒有 StreamWriter: https://dotnetfiddle.net/Nno0DF

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM