2016-05-10 100 views
1

我爲每個用戶生成隨機HMAC密鑰並將密鑰存儲在我們的數據庫中。如果用戶請求密鑰,用戶只能獲得密鑰,並且通常只是使用我們的API令牌(SWT)作爲BASE64編碼的不透明密鑰,而不必擔心它們的完整性。爲什麼我不能加密/解密數據庫存儲的HMAC密鑰?

我想在將密鑰存儲到我們的SQL Server數據庫之前對其進行加密,以防止密鑰泄露。它們的加密密鑰存儲在varbinary(MAX)列中。沒有加密,一切都很好。

我使用AES進行加密,隨機生成的IV存儲在加密值的開頭。

在我的單元測試中,使用簡單的字符串,一切都很好,但是,對於HMAC密鑰,解密值永遠不會與原始值匹配。如果我生成一個HMAC密鑰,加密它,將它存儲在數據庫中。當我檢索它時,解密它,並使用它們鍵來生成一個HMAC散列,它與原始HMAC散列值不匹配。

請參閱下面的加密/解密方法。

public static byte[] Encrypt(byte[] value) 
    { 
     using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) 
     { 
      Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(_password, Encoding.ASCII.GetBytes(_salt)); 

      aes.Key = key.GetBytes(aes.KeySize/8); 
      aes.GenerateIV(); 
      aes.Padding = PaddingMode.PKCS7; 
      aes.Mode = CipherMode.CBC; 

      using (var crypt = aes.CreateEncryptor(aes.Key, aes.IV)) 
      using (MemoryStream ms = new MemoryStream()) 
      { 
       using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Write)) 
       { 
        cs.Write(aes.IV, 0, aes.IV.Length); 

        using (BinaryWriter bw = new BinaryWriter(cs)) 
        {        
         bw.Write(value); 
         cs.FlushFinalBlock(); 
        } 

        return ms.ToArray(); 
       } 
      } 
     } 
    } 

public static byte[] Decrypt(byte[] value) 
    { 
     using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) 
     { 
      Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(_password, Encoding.ASCII.GetBytes(_salt)); 

      aes.Key = key.GetBytes(aes.KeySize/8); 
      aes.Padding = PaddingMode.PKCS7; 
      aes.Mode = CipherMode.CBC; 

      using (MemoryStream ms = new MemoryStream(value)) 
      { 
       byte[] iv = new byte[aes.IV.Length]; 

       ms.Read(iv, 0, aes.IV.Length); 
       aes.IV = iv; 

       using (var crypt = aes.CreateDecryptor(aes.Key, aes.IV)) 
       using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Read)) 
       { 
        using (StreamReader sr = new StreamReader(cs)) 
         return Encoding.ASCII.GetBytes(sr.ReadToEnd()); 
       } 
      } 
     } 
    } 

密碼和salt存儲在編譯爲代碼的常量字符串文字中。我意識到這並不理想,但現在是這樣。

+0

你已經給出了一些代碼,但沒有說它不工作的方式... –

+0

如果我生成一個HMAC密鑰,加密它,將它存儲在數據庫中。當我檢索它時,解密它,並使用它們鍵來生成一個HMAC散列,它與原始HMAC散列值不匹配。 –

+0

那麼你是如何診斷問題的呢?你記錄了每個階段涉及的字節嗎? (在加密之前,在保存到數據庫之前進行加密之後,從數據庫中獲取之後但在解密之前,在解密之後......)基本上,您需要確切地確定造成問題的階段。 –

回答

0

我認爲這個問題有兩個問題。首先,正如Jon Skeet所討論的那樣,IV被加密,因此用於解密值時是不同的。我通過直接寫入MemoryStream而不是CryptoStream來糾正以下代碼。

public static byte[] Encrypt(byte[] value) 
    { 
     using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) 
     { 
      Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(_password, Encoding.ASCII.GetBytes(_salt)); 

      aes.Key = key.GetBytes(aes.KeySize/8); 
      aes.GenerateIV(); 
      aes.Padding = PaddingMode.PKCS7; 
      aes.Mode = CipherMode.CBC; 

      using (var crypt = aes.CreateEncryptor(aes.Key, aes.IV)) 
      using (MemoryStream ms = new MemoryStream()) 
      using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Write)) 
      using (BinaryWriter bw = new BinaryWriter(cs)) 
      { 
       ms.Write(aes.IV, 0, aes.IV.Length); 
       bw.Write(value); 
       cs.FlushFinalBlock(); 

       return ms.ToArray(); 
      } 
     } 
    } 

接下來的部分我不能完全肯定,但我認爲喬恩也對那裏是一些問題與讀入一個字符串,並返回一個字節數組正確。我通過使用類似於他在這裏找到的一些代碼來糾正這個問題:jonskeet.uk/csharp/readbinary.html直接讀取一個流到一個字節數組中。請參閱以下代碼,其中ReadStream()是我根據Jon編寫的方法。

public static byte[] Decrypt(byte[] value) 
    { 
     using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) 
     { 
      Rfc2898DeriveBytes key = new Rfc2898DeriveBytes(_password, Encoding.ASCII.GetBytes(_salt)); 

      aes.Key = key.GetBytes(aes.KeySize/8); 
      aes.Padding = PaddingMode.PKCS7; 
      aes.Mode = CipherMode.CBC; 

      using (MemoryStream ms = new MemoryStream(value)) 
      { 
       byte[] iv = new byte[aes.IV.Length]; 

       ms.Read(iv, 0, aes.IV.Length); 
       aes.IV = iv; 

       using (var crypt = aes.CreateDecryptor(aes.Key, aes.IV)) 
       using (CryptoStream cs = new CryptoStream(ms, crypt, CryptoStreamMode.Read)) 
        return ReadStream(cs, 0, ms.Length); 
      } 
     } 
    } 

感謝您的幫助喬恩。我也非常感謝你不只是放棄它。如果我要學習一些東西,我不介意爲它工作。這是我得到的報酬。

-1

如何簡單和自定義?

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

namespace GrimoireTactics.Framework.Security 
{ 
    public enum ObfuscatorType 
    { 
     Encrypt, 
     Decrypt 
    } 
    public class Obfuscator 
    { 
     private string _seed; 
     private byte[] _hashedSeedBytes; 
     private readonly SHA256Managed _hashingAlgorithm; 
     public string Seed 
     { 
      get 
      { 
       return _seed; 
      } 
      set 
      { 
       this._seed = value; 
       SeedHash = GenerateHash(value); 
       this._hashedSeedBytes = GetBytes(SeedHash); 
      } 
     } 

     public byte[] SeedBytes 
     { 
      get 
      { 
       return _hashedSeedBytes; 
      } 
     } 

     public string SeedHash { get; private set; } 

     public Obfuscator(string seed) 
     { 
      this._hashingAlgorithm = new SHA256Managed(); 
      this.Seed = seed; 
     } 


     public byte[] Encrypt(byte[] data) 
     { 
      return Transform(data, ObfuscatorType.Encrypt); 
     } 

     public byte[] Encrypt(string data) 
     { 
      return Transform(GetBytes(data), ObfuscatorType.Encrypt); 
     } 

     public byte[] Decrypt(byte[] data) 
     { 
      return Transform(data, ObfuscatorType.Decrypt); 
     } 

     public byte[] Transform(byte[] bytes, ObfuscatorType type) 
     { 
      int passwordShiftIndex = 0; 
      byte[] data = bytes; 
      byte offset = 0; 
      switch (type) 
      { 
       case ObfuscatorType.Encrypt: 
        for (int i = 0; i < data.Length; i++) 
        { 
         byte currentByte = _hashedSeedBytes[passwordShiftIndex]; 
         offset += (byte)(1 + currentByte); // Incrementing Offset 
         data[i] = (byte)(data[i] + currentByte + offset); 
         passwordShiftIndex = (passwordShiftIndex + 1) % _hashedSeedBytes.Length; 
        } 
        break; 
       case ObfuscatorType.Decrypt: 
        for (int i = 0; i < data.Length; i++) 
        { 
         byte currentByte = _hashedSeedBytes[passwordShiftIndex]; 
         offset += (byte)(1 + currentByte); // Incrementing Offset 
         data[i] = (byte)(data[i] - currentByte - offset); 
         passwordShiftIndex = (passwordShiftIndex + 1) % _hashedSeedBytes.Length; 
        } 
        break; 
      } 
      return data; 
     } 

     public byte[] GetBytes(string data) 
     { 
      return Encoding.UTF8.GetBytes(data); 
     } 

     public byte[] GetBytes(string data, Encoding encoding) 
     { 
      return encoding.GetBytes(data); 
     } 

     public string GetString(byte[] data) 
     { 
      return Encoding.UTF8.GetString(data); 
     } 

     public string GetString(byte[] data, Encoding encoding) 
     { 
      return encoding.GetString(data); 
     } 

     public string GenerateHash(string text) 
     { 
      byte[] bytes = Encoding.UTF8.GetBytes(text); 
      byte[] hash = _hashingAlgorithm.ComputeHash(bytes); 
      string hashString = string.Empty; 
      for (int index = 0; index < hash.Length; index++) 
      { 
       byte x = hash[index]; 
       hashString += $"{x:x2}"; 
      } 
      return hashString; 
     } 
    } 
} 

用例:

using System.Diagnostics; 
using System.IO; 
using System.Windows.Forms; 
using GrimoireDevelopmentKit.DevelopmentKit.UserInterface; 
using GrimoireTactics.Framework.OpenGL.Modeling; 
using GrimoireTactics.Framework.OpenGL.Texturing; 
using GrimoireTactics.Framework.Security; 

namespace GrimoireDevelopmentKit.DevelopmentKit 
{ 
    static class Program 
    { 
     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     [STAThread] 
     static void Main() 
     { 
      //Application.EnableVisualStyles(); 
      //Application.SetCompatibleTextRenderingDefault(false); 
      //Application.Run(new DevelopmentKitEditor()); 

      Obfuscator obs = new Obfuscator("My Arbitary Seed"); 
      byte[] obufsicatedData = obs.Encrypt("Some Top Secret Data"); 
      byte[] unobufsicatedData = obs.Decrypt(obufsicatedData); 
      Console.WriteLine(obs.GetString(unobufsicatedData)); 
      Console.Read(); 
     } 
    } 
} 

我們所要做的是混淆使用自定義算法的字節數。代碼可供任何人免費使用;我只是把它作爲一個附加的安全措施。

+0

謝謝你的建議,但我沒有真正尋找比AES更弱的東西。我現在有AES設計,但無論如何感謝。 –

+0

@EvilAugust有「安全」的不同措施。 AES與我的自定義算法同樣安全,因爲不可能隱藏安全密鑰。最後,障礙是最重要的。我的代碼可以快速加密和解密,並提供1:1的大小比例。另外,算法的複雜性使得破解方法耗費太多時間。 – Krythic

相關問題