I'm attempting to implement a solution to hash and salt a password in SQL Server 2008 using the SHA-512 algorithm. This solution is based on the book "Expert SQL Server 2008 Encryption" by Michael Coles. Based on his examples, I am able to build the project in visual studio 2010 (.NET 3.5 in C#) and deploy to SQL Server 2008 (as shown in the code below).
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);
}
}
}
When I perform a test from SQL using the code below, the hash is generated as expected for each algorithm.
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;
I would like to use this for validating a login, where I'm assuming I would need to retrieve the salt value that is stored for the user record and pass it along to the SaltedHash function, where it would return the hashed value. From there, I would compare the hashed value returned by the function with the hashed value stored in the user record.
The problem I'm encountering is when I test passing a hardcoded salt value ('0x0E5ECC235FF6BD7337FFDDE5799D4EEA'), along with the plaintext ('ABCDEFGHIJ') to simulate retrieving the hashed value (for comparing hashed passwords for example). If I provide the plaintext value of ('1234567890') with the same hardcoded salt value, it returns the exact same hashed value. Actually, any 10 character plaintext value returns the same hashed value.
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;
I'm assuming the problem lies with the "combining the plaintext with the salt" code, but unsure.
Any ideas as to how to solve this problem?