2013-10-30 61 views
3

我正在嘗試使用SHA-512算法實現SQL Server 2008中的散列和密碼解決方案。此解決方案基於Michael Coles出版的書籍「Expert SQL Server 2008 Encryption」。基於他的例子,我能夠在Visual Studio 2010(C#中的.NET 3.5)中構建項目並部署到SQL Server 2008(如下面的代碼所示)。使用SQL Server 2008密碼散列CLR

using System; 
using System.Data; 
using System.Data.SqlClient; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 
using System.Security.Cryptography; 

namespace Apress.Samples 
{ 
    public partial class CustomEncryption 
    { 
    [Microsoft.SqlServer.Server.SqlFunction 
    (
     IsDeterministic = true, 
     DataAccess = DataAccessKind.None 
    )] 
    public static SqlBytes SaltedHash 
    (
     SqlString Algorithm, 
     [SqlFacet(MaxSize = -1)] SqlBytes PlainText, 
     SqlBytes Salt 
    ) 
    { 
     // Return NULL if any of the parameters is NULL 
     if (Algorithm.IsNull || PlainText.IsNull || Salt.IsNull) 
      return SqlBytes.Null; 

     // Determine which algorithm to use 
     bool HashDefined = true; 
     HashAlgorithm Hash = null; 
     switch (Algorithm.Value.ToUpper()) 
     { 
      case "SHA256": 
       Hash = new SHA256Managed(); 
       break; 

      case "SHA384": 
       Hash = new SHA384Managed(); 
       break; 

      case "SHA512": 
       Hash = new SHA512Managed(); 
       break; 

      default: 
       HashDefined = false; 
       break; 
     } 
     if (!HashDefined) 
      throw new Exception 
       ("Unsupported hash algorithm - use SHA256, SHA384 or SHA512"); 

     // Combine the plaintext with the salt 
     byte[] PlainTextWithSalt = new byte[PlainText.Length + Salt.Length]; 
     for (long i = 0; i < Salt.Length; i++) 
      PlainTextWithSalt[i] = Salt[i]; 
     for (long i = Salt.Length; i < PlainText.Length; i++) 
      PlainTextWithSalt[i] = PlainText.Value[i - Salt.Length]; 

     // Generate the hash and return the result 
     byte[] HashBytes = Hash.ComputeHash(PlainTextWithSalt); 
     return new SqlBytes(HashBytes); 
    } 
} 
} 

當我使用下面的代碼從SQL執行測試時,按照預期爲每個算法生成哈希。

DECLARE @plaintext varchar(15); 
SET @plaintext = 'ABCDEFGHIJ'; 

DECLARE @salt varbinary(16); 
SET @salt = Crypt_Gen_Random(16); 

DECLARE @sha256 varbinary(32) 
DECLARE @sha384 varbinary(48) 
DECLARE @sha512 varbinary(64) 

SELECT @sha256 = dbo.SaltedHash('SHA256', CAST(@plaintext AS varbinary(max)), @salt); 
SELECT @sha384 = dbo.SaltedHash('SHA384', CAST(@plaintext AS varbinary(max)), @salt); 
SELECT @sha512 = dbo.SaltedHash('SHA512', CAST(@plaintext AS varbinary(max)), @salt); 

SELECT 'SHA-256' AS algorithm, @sha256 AS hash 
UNION ALL 
SELECT 'SHA-384', @sha384 
UNION ALL 
SELECT 'SHA-512', @sha512; 

我想用這個驗證登錄,在這裏我假設我需要檢索存儲在用戶記錄中的鹽值並將其傳遞到SaltedHash功能,哪裏會返回哈希值。從那裏,我將比較函數返回的哈希值和存儲在用戶記錄中的哈希值。

我遇到的問題是當我測試傳遞一個硬編碼鹽值('0x0E5ECC235FF6BD7337FFDDE5799D4EEA'),以及明文('ABCDEFGHIJ')模擬檢索哈希值(例如比較散列密碼)。如果我使用相同的硬編碼salt值提供明文值('1234567890'),它將返回完全相同的哈希值。實際上,任何10個字符的明文值都會返回相同的哈希值。

DECLARE @plaintext varchar(15); 
SET @plaintext = 'ABCDE12345'; 

DECLARE @salt varbinary(16); 
SET @salt = 0x0E5ECC235FF6BD7337FFDDE5799D4EEA; // Hardcoded salt value! 

SELECT @salt 

DECLARE @sha256 varbinary(32) 
DECLARE @sha384 varbinary(48) 
DECLARE @sha512 varbinary(64) 

SELECT @sha256 = dbo.SaltedHash('SHA256', CAST(@plaintext AS varbinary(max)), @salt); 
SELECT @sha384 = dbo.SaltedHash('SHA384', CAST(@plaintext AS varbinary(max)), @salt); 
SELECT @sha512 = dbo.SaltedHash('SHA512', CAST(@plaintext AS varbinary(max)), @salt); 

SELECT 'SHA-256' AS algorithm, @sha256 AS hash 
UNION ALL 
SELECT 'SHA-384', @sha384 
UNION ALL 
SELECT 'SHA-512', @sha512; 

我假設問題在於「結合明文與鹽」的代碼,但不確定。

有關如何解決此問題的任何想法?

+0

請注意,如果鹽在您的最終解決方案中進行了硬編碼,則它不是鹽。這將是一個[Pepper],鹽每個密碼都是唯一的,你通常會只需將salt存儲在下一列中,或將其連接到密文字節陣列的前面或後面。 –

+0

謝謝你的提示,因爲我不熟悉'胡椒'。我的意圖是按照您的建議爲每個密碼創建一個獨特的salt。 – vanexellent

回答

2

是的,您在陣列副本中發生了錯誤,您絕不會將最後一個Salt.Length字節的PlainText複製到目標陣列中。這是您的原始代碼的一個更正版本。所有這一切需要做的是i需求要上去PlainText.Length + Salt.Length

// Combine the plaintext with the salt 
byte[] PlainTextWithSalt = new byte[PlainText.Length + Salt.Length]; 
for (long i = 0; i < Salt.Length; i++) 
    PlainTextWithSalt[i] = Salt[i]; 
for (long i = Salt.Length; i < PlainText.Length + Salt.Length; i++) 
    PlainTextWithSalt[i] = PlainText.Value[i - Salt.Length]; 

然而,它是一個更容易通過使用其他Array.Copy

byte[] PlainTextWithSalt = new byte[PlainText.Length + Salt.Length]; 
Array.Copy(Salt, PlainTextWithSalt, Salt.Length); 
Array.Copy(PlainText, PlainTextWithSalt, Salt.Length, PlainText.Length); 

有一點需要注意的事情。醃製你的密碼是好的,但是that is only half the battle,你還需要做散列SLOW。通常這是通過bcryptPBKDF2這些函數完成的,這些函數管理混合salt和密碼,另外它們還允許您設置密碼的哈希迭代次數(通常會選擇一個大數字,您希望數量儘可能大,因爲您的終端系統的緩慢)。

+0

我已經應用了您更正的代碼並確認它正在運行。另外,感謝您的功課。我決定使用CLR/.NET SHA2方法,因爲它似乎是一種被廣泛採用的解決方案。但似乎bcrypt也是我會投入一些時間的東西。bcrypt的'調優'部分確實看起來很有吸引力。 – vanexellent

+0

@vanexellent PBKDF2也具有通過它的[ItterationCount](http://msdn.microsoft.com/en-us/library/system.security.cryptography.rfc2898derivebytes.iterationcount.aspx)屬性內置的調整因子然而,你會希望使用數字'> 10000'來計數,以在現代硬件上獲得有意義的算法減速)使用可調工作因子的好處在於,當你獲得更快的硬件並且需要使得哈希更慢時才重新哈希在用戶下次登錄時有更大的係數,所有你需要做的就是存儲你上一次使用哈希和鹽的工作因子 –

+0

謝謝,我已經對這些算法做了一些研究,很高興知道它們可以是從SQL 2008(.NET 3.5)中使用,這是我們現在堅持使用的版本。感謝幫助! – vanexellent