2016-05-10 68 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

相关问题