2014-07-23 46 views
1

我沒有安全背景,並且我已經處理的最多密碼術是使用從網上拾取的片段。經過一番閱讀後,很明顯我需要更好地理解這些算法的工作原理。所以請忍受任何天真。使用有效值初始化SymmetricAlgorithm

我試圖實現一些普通/抽象的方法,期望完全初始化SymmetricAlgorithm實例。據我所見,轉換方法可以很好地執行,而調用代碼則沒有。我得到的例外情況如下等等:

The input data is not a complete block.
Length of the data to decrypt is invalid.

用法:

using System; 
using System.Security.Cryptography; 
using System.Text; 

public class Program 
{ 
    private static void Main (string [] args) 
    { 
     var cycled = ""; 
     Exception exception = null; 
     var output = new byte [] { }; 
     var encoding = Encoding.UTF8; 
     var source = "Dude, where's my car?!"; 

     var algorithm = RijndaelManaged.Create(); 
     algorithm.GenerateIV(); 
     algorithm.GenerateKey(); 
     algorithm.Mode = CipherMode.CBC; // CipherMode.OFB; 

     // I've tried multiple combinations of algorithms and modes, which all result in exceptions. 

     if (SecurityUtilities.Encrypt(source, encoding, algorithm, out output, out exception)) 
     { 
      if (SecurityUtilities.Decrypt(output, encoding, algorithm, out cycled, out exception)) 
      { 
       var r = cycled == source; 
       Console.Write("{0} = {1} == {2}.", r, source, cycled); 
      } 
      else 
      { 
       Console.Write(exception); 
      } 
     } 
     else 
     { 
      Console.Write(exception); 
     } 

     Console.ReadKey(); 
    } 
} 

通用代碼:

using System; 
using System.IO; 
using System.Linq; 
using System.Security.Cryptography; 
using System.Text; 

public static class SecurityUtilities 
{ 
    public static bool Encrypt (string source, Encoding encoding, SymmetricAlgorithm algorithm, out string output, out Exception exception) 
    { 
     var result = false; 

     output = ""; 
     exception = null; 

     if (source == null) { throw (new ArgumentNullException("source")); } 
     if (encoding == null) { throw (new ArgumentNullException("encoding")); } 
     if (algorithm == null) { throw (new ArgumentNullException("algorithm")); } 

     try 
     { 
      var bytesEncrypted = new byte [] { }; 
      var bytesSource = encoding.GetBytes(source); 

      if (SecurityUtilities.Encrypt(bytesSource, algorithm, out bytesEncrypted, out exception)) 
      { 
       output = encoding.GetString(bytesEncrypted); 

       result = true; 
      } 
     } 
     catch (Exception e) 
     { 
      exception = e; 
     } 

     return (result); 
    } 

    public static bool Encrypt (string source, Encoding encoding, SymmetricAlgorithm algorithm, out byte [] output, out Exception exception) 
    { 
     var result = false; 

     output = null; 
     exception = null; 

     if (source == null) { throw (new ArgumentNullException("source")); } 
     if (encoding == null) { throw (new ArgumentNullException("encoding")); } 
     if (algorithm == null) { throw (new ArgumentNullException("algorithm")); } 

     try 
     { 
      var bytesEncrypted = new byte [] { }; 
      var bytesSource = encoding.GetBytes(source); 

      if (SecurityUtilities.Encrypt(bytesSource, algorithm, out bytesEncrypted, out exception)) 
      { 
       output = bytesEncrypted; 

       result = true; 
      } 
     } 
     catch (Exception e) 
     { 
      exception = e; 
     } 

     return (result); 
    } 

    public static bool Encrypt (byte [] source, SymmetricAlgorithm algorithm, out byte [] output, out Exception exception) 
    { 
     var result = false; 

     output = null; 
     exception = null; 

     if (source == null) { throw (new ArgumentNullException("source")); } 
     if (algorithm == null) { throw (new ArgumentNullException("algorithm")); } 

     try 
     { 
      using (var memoryStream = new MemoryStream()) 
      { 
       using (var transform = algorithm.CreateEncryptor()) 
       { 
        using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) 
        { 
         cryptoStream.Write(source, 0, source.Length); 
        } 
       } 

       output = memoryStream.ToArray(); 
      } 

      result = true; 
     } 
     catch (Exception e) 
     { 
      exception = e; 
     } 

     return (result); 
    } 

    public static bool Decrypt (string source, Encoding encoding, SymmetricAlgorithm algorithm, out string output, out Exception exception) 
    { 
     var result = false; 

     output = ""; 
     exception = null; 

     if (source == null) { throw (new ArgumentNullException("source")); } 
     if (encoding == null) { throw (new ArgumentNullException("encoding")); } 
     if (algorithm == null) { throw (new ArgumentNullException("algorithm")); } 

     try 
     { 
      var bytesDecrypted = new byte [] { }; 
      var bytesSource = encoding.GetBytes(source); 

      if (SecurityUtilities.Decrypt(bytesSource, algorithm, out bytesDecrypted, out exception)) 
      { 
       output = encoding.GetString(bytesDecrypted); 

       result = true; 
      } 
     } 
     catch (Exception e) 
     { 
      exception = e; 
     } 

     return (result); 
    } 

    public static bool Decrypt (byte [] source, Encoding encoding, SymmetricAlgorithm algorithm, out string output, out Exception exception) 
    { 
     var result = false; 

     output = ""; 
     exception = null; 

     if (source == null) { throw (new ArgumentNullException("source")); } 
     if (encoding == null) { throw (new ArgumentNullException("encoding")); } 
     if (algorithm == null) { throw (new ArgumentNullException("algorithm")); } 

     try 
     { 
      var bytesSource = source; 
      var bytesDecrypted = new byte [] { }; 

      if (SecurityUtilities.Decrypt(bytesSource, algorithm, out bytesDecrypted, out exception)) 
      { 
       output = encoding.GetString(bytesDecrypted); 

       result = true; 
      } 
     } 
     catch (Exception e) 
     { 
      exception = e; 
     } 

     return (result); 
    } 

    public static bool Decrypt (byte [] source, SymmetricAlgorithm algorithm, out byte [] output, out Exception exception) 
    { 
     var result = false; 

     output = null; 
     exception = null; 

     if (source == null) { throw (new ArgumentNullException("source")); } 
     if (algorithm == null) { throw (new ArgumentNullException("algorithm")); } 

     try 
     { 
      using (var memoryStream = new MemoryStream(source)) 
      { 
       using (var transform = algorithm.CreateDecryptor()) 
       { 
        using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Read)) 
        { 
         cryptoStream.Read(source, 0, source.Length); 
        } 
       } 

       output = memoryStream.ToArray(); 
      } 

      result = true; 
     } 
     catch (Exception e) 
     { 
      exception = e; 
     } 

     return (result); 
    } 
} 

核心問題是的,在Encrypt/Decrypt方法中是否存在錯誤?如果不存在,調用代碼中需要的最低限度更改才能使其正常工作?

+0

你的異常處理是有問題的。讓他們冒泡而不是將它們作爲價值回報。這種模式對密碼學來說尤其危險,但一般來說不好。 – usr

+0

@usr:'TryXXX(out exp)'模式是庫的其餘部分使用的,所以我無法控制它。但是你爲什麼說這對冷印術來說特別不好? –

+1

因爲調用者可以無意中忽略返回值並繼續執行無效數據。如果安全決策依賴於(可能使用加密代碼),那可能是災難性的。 – usr

回答

2

您提到的兩個錯誤是同一問題的結果 - 許多對稱算法是「塊密碼」,即它們在一個或多個特定大小的塊上工作。使用分組密碼加密任意長度的數據時,必須填充長度以使其長度爲塊大小的倍數,類似地,在解密時,必須刪除填充以提供原始未填充的明文。

有許多不同的方式來填充明文,並且使用的方法由SymmetricAlgorithmPadding屬性確定。如果設置爲PaddingMode.None,那麼明文必須爲的長度爲BlockSize的倍數。

如果我們假設SymmetricAlgorithm實例設置爲使用除None以外的填充模式,那麼有必要告訴算法,您已完成加密並且應該添加填充(或者解密時,您已經讀取了所有密文,它應該刪除填充)。當使用CryptoStream這是使用FlushFinalBlock()方法實現,應該是所有的數據已被寫入流後立即執行:

using (var memoryStream = new MemoryStream()) 
{ 
    using (var transform = algorithm.CreateEncryptor()) 
    { 
     using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) 
     { 
      cryptoStream.Write(source, 0, source.Length); 
      // Tell the CryptoStream we've written all the data and padding should be applied 
      cryptoStream.FlushFinalBlock(); 
     } 
    } 
    output = memoryStream.ToArray(); 
} 

在你Decrypt(byte[], SymmetricAlgorithm, ...)您使用memoryStream.ToArray()爲您output,但是memoryStream包含密文,不是由此產生的明文。而應該能夠使用相同結構的加密,除了使用transform.CreateDecryptor()代替transform.CreateEncryptor()

using (var memoryStream = new MemoryStream()) 
{ 
    using (var transform = algorithm.CreateDecryptor()) 
    { 
     using (var cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write)) 
     { 
      cryptoStream.Write(source, 0, source.Length); 
      // Tell the CryptoStream we've written all the data and padding should be removed 
      cryptoStream.FlushFinalBlock(); 
     } 
    } 
    output = memoryStream.ToArray(); 
} 

最後,在Encrypt (string, Encoding, ...)你服用的二進制密文,並使用encoding.GetString()其轉換爲字符串,無效。如果您希望將二進制數據表示爲字符串,則應該使用類似Convert.ToBase64String()的內容。同樣在Decrypt(string, Encoding, ...)中,您應該使用Convert.FromBase64String()將傳入的Base64編碼密文字符串轉換爲解密所需的字節數組。

+0

謝謝。使用哪種填充模式有沒有關係?不是從哪個角度來看「更好」,而是從功能角度來看。 –

+0

@RaheelKhan通常的選擇是'PKCS7'。我會避免使用'Zeros',因爲如果您的數據本身可能包含尾隨的零字節,則它可能是不明確的。 – Iridium