How to convert varbinary to GUID in TSQL stored procedure? - sql-server

how can I convert the HASHBYTES return value to a GUID?
This is what I have so far.
CREATE PROCEDURE [dbo].[Login]
#email nvarchar,
#password varchar
AS
BEGIN
DECLARE #passHashBinary varbinary;
DECLARE #newPassHashBinary varbinary;
-- Create a unicode (utf-16) password
Declare #unicodePassword nvarchar;
Set #unicodePassword = CAST(#password as nvarchar);
SET #passHashBinary = HASHBYTES('md5', #password);
SET #newPassHashBinary = HASHBYTES('md5', #unicodePassword);

Simply cast it:
select cast(hashbytes('md5','foo') as uniqueidentifier)
But there are two questions lingering:
why cast HASHBYTES to guid? Why not use the appropriate type for storage, namely BINARY(16)
I hope you are aware that MD5 hashing passwords is basically useless, right? Because of rainbow tables. You need to use a secure hashing scheme, like an HMAC or the HA1 of Digest.

Related

Convert UTF-8 varbinary(max) to varchar(max)

I have a varbinary(max) column with UTF-8-encoded text that has been compressed. I would like to decompress this data and work with it in T-SQL as a varchar(max) using the UTF-8 capabilities of SQL Server.
I'm looking for a way of specifying the encoding when converting from varbinary(max) to varchar(max). The only way I've managed to do that is by creating a table variable with a column with a UTF-8 collation and inserting the varbinary data into it.
DECLARE #rv TABLE(
Res varchar(max) COLLATE Latin1_General_100_CI_AS_SC_UTF8
)
INSERT INTO #rv
SELECT SUBSTRING(Decompressed, 4, DATALENGTH(Decompressed) - 3) WithoutBOM
FROM
(SELECT DECOMPRESS(RawResource) AS Decompressed FROM Resource) t
I'm wondering if there is a more elegant and efficient approach that does not involve inserting into a table variable.
UPDATE:
Boiling this down to a simple example that doesn't deal with byte order marks or compression:
I have the string "Hello 😊" UTF-8 encoded without a BOM stored in variable #utf8Binary
DECLARE #utf8Binary varbinary(max) = 0x48656C6C6F20F09F988A
Now I try to assign that into various char-based variables and print the result:
DECLARE #brokenVarChar varchar(max) = CONVERT(varchar(max), #utf8Binary)
print '#brokenVarChar = ' + #brokenVarChar
DECLARE #brokenNVarChar nvarchar(max) = CONVERT(varchar(max), #utf8Binary)
print '#brokenNVarChar = ' + #brokenNVarChar
DECLARE #rv TABLE(
Res varchar(max) COLLATE Latin1_General_100_CI_AS_SC_UTF8
)
INSERT INTO #rv
select #utf8Binary
DECLARE #working nvarchar(max)
Select TOP 1 #working = Res from #rv
print '#working = ' + #working
The results of this are:
#brokenVarChar = Hello 😊
#brokenNVarChar = Hello 😊
#working = Hello 😊
So I am able to get the binary result properly decoded using this indirect method, but I am wondering if there is a more straightforward (and likely efficient) approach.
I don't like this solution, but it's one I got to (I initially thought it wasn't working, due to what appears to be a bug in ADS). One method would be to create a new database in a UTF8 collation, and then pass the value to a function in that database. As the database is in a UTF8 collation, the default collation will be different to the local one, and the correct result will be returned:
CREATE DATABASE UTF8 COLLATE Latin1_General_100_CI_AS_SC_UTF8;
GO
USE UTF8;
GO
CREATE OR ALTER FUNCTION dbo.Bin2UTF8 (#utfbinary varbinary(MAX))
RETURNS varchar(MAX) AS
BEGIN
RETURN CAST(#utfbinary AS varchar(MAX));
END
GO
USE YourDatabase;
GO
SELECT UTF8.dbo.Bin2UTF8(0x48656C6C6F20F09F988A);
This, however, isn't particularly "pretty".
There is an undocumented hack:
DECLARE #utf8 VARBINARY(MAX)=0x48656C6C6F20F09F988A;
SELECT CAST(CONCAT('<?xml version="1.0" encoding="UTF-8" ?><![CDATA[',#utf8,']]>') AS XML)
.value('.','nvarchar(max)');
The result
Hello 😊
This works even in versions without the new UTF8 collations...
UPDATE: calling this as a function
This can easily be wrapped in a scalar function
CREATE FUNCTION dbo.Convert_UTF8_Binary_To_NVarchar(#utfBinary VARBINARY(MAX))
RETURNS NVARCHAR(MAX)
AS
BEGIN
RETURN
(
SELECT CAST(CONCAT('<?xml version="1.0" encoding="UTF-8" ?><![CDATA[',#utfBinary,']]>') AS XML)
.value('.','nvarchar(max)')
);
END
GO
Or like this as an inlined table valued function
CREATE FUNCTION dbo.Convert_UTF8_Binary_To_NVarchar(#utfBinary VARBINARY(MAX))
RETURNS TABLE
AS
RETURN
SELECT CAST(CONCAT('<?xml version="1.0" encoding="UTF-8" ?><![CDATA[',#utfBinary,']]>') AS XML)
.value('.','nvarchar(max)') AS ConvertedString
GO
This can be used after FROM or - more appropriate - with APPLY
DECLARE #utf8Binary varbinary(max) = 0x48656C6C6F20F09F988A;
DECLARE #brokenNVarChar nvarchar(max) = concat(#utf8Binary, '' COLLATE Latin1_General_100_CI_AS_SC_UTF8);
print '#brokenNVarChar = ' + #brokenNVarChar;
You didn't say how your data is compressed or what compression algorithm was used. But if you are using the COMPRESS function in SQL Server 2016 or later, you can use the DECOMPRESS function and then cast it to a VARCHAR(MAX). Both COMPRESS and DECOMPRESS use the GZip compression algorithm.
This function will decompress an input expression value, using the GZIP algorithm. DECOMPRESS will return a byte array (VARBINARY(MAX) type).
CAST(DECOMPRESS([compressed content here]) AS VARCHAR(MAX))
See: COMPRESS (Transact-SQL) and DECOMPRESS (Transact-SQL)

How to store a string along with Syllabication in varchar column

Is there any way to store āre exactly in SQL server table.
I hardcoded the same value in varchar column. It is saving are. I wanted to store along with special symbols
Use Nvarchar - Nvarchar stores UNICODE data. If you have requirements to store UNICODE or multilingual data, Nvarchar is the choice. You need an N prefix when inserts data. Varchar stores ASCII data.
Refer below sample code
declare #data table
(field1 nvarchar(10))
insert into #data
values
(N'āre')
select * from #data
You need to declare your string assignment using the N prefix (the N
stands for "National Character") as you need to explicitly say you are
passing a string containing unicode characters here (or an nchar,
ntext etc if you were using those).
NVarchar variable are denoted by N' so it would be
DECLARE #objname nvarchar(255)
set #objname=N'漢字'
select #objname
Now the output will be 漢字 as it has been set. Run above code.

Create a Stored Procedure for AES Encryption in MS SQL Server 2008

I have an SQL Server 2008 table with a structure similar to the following:
ID int PRIMARY KEY IDENTITY(1,1)
Name nvarchar(100)
LongText ntext
What I am trying to achieve is simple. Before inserting data inside this table, I want to encrypt the LongText using AES_192 algorithm. I am using the following SP to encrypt data:
create proc sp_Encrypt_LongText
#rawText ntext = null,
#encryptedText nvarchar(max) output
as
begin
OPEN SYMMETRIC KEY Encryption_Symmetric_Key
DECRYPTION BY CERTIFICATE Encryption_Certificate WITH PASSWORD = 'mypassword'
set #encryptedText = ENCRYPTBYKEY(KEY_GUID(N'Encryption_Symmetric_Key'), cast(#rawText as nvarchar(max)))
CLOSE SYMMETRIC KEY Encryption_Symmetric_Key
end
and for decryption, I have created the following SP:
alter proc sp_Decrypt_LongText
#encryptedText ntext = null,
#decryptedText varchar(max) output
as
begin
OPEN SYMMETRIC KEY Encryption_Symmetric_Key
DECRYPTION BY CERTIFICATE Encryption_Certificate WITH PASSWORD = 'mypassword'
set #decryptedText = cast(DECRYPTBYKEY(cast(#encryptedText as nvarchar(max))) as varchar(max))
CLOSE SYMMETRIC KEY Encryption_Symmetric_Key
end
The procedures seem to work fine when I use the exec command. So far, so good. The problem is that the data is inserted and fetched inside the table using stored procedures; one each for insert and select. What I have as of now is as follows:
For insertion:
create proc sp_InsertData
#Name nvarchar(100),
#LongText ntext = NULL
as
INSERT INTO TABLE tbl VALUES (#Name, #LongText)
For fetching
create proc sp_FindDataById
#Id int
as
SELECT ID, Name, LongText from tbl where ID=#Id
My question is, how do I plug the encryption/decryption procedures inside these SPs to make them work?. I have looked into several articles for achieving this, but I keep running into one issue or another; mostly because of the ntext datatype. Or maybe I might be going on the wrong path here. Any kind of help is appreciated.
PS: Due to some reasons specified by the DBAs, I can't change the data type of LongText from ntext to nvarchar or varchar. Hence, all the casting is applied in the procedures.
Okay, so I managed to convince the DBAs to have the data transferred to a new column with varbinary(max) data type. Then I transferred the values into this new column after encrypting them, and then dropped the older column and renamed the new one to the old one's name. Took some work, but everything is running smoothly now. I managed to create a stored procedure and two functions to further modularize the scripts.
For opening the symmetric key
CREATE PROCEDURE sp_OpenEncryptionKeys
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRY
OPEN SYMMETRIC KEY Encryption_Symmetric_Key
DECRYPTION BY CERTIFICATE Encryption_Certificate
END TRY
BEGIN CATCH
--catch
END CATCH
END
For encrypting:
CREATE FUNCTION Encrypt
(
#ValueToEncrypt varchar(max)
)
RETURNS varbinary(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varbinary(max)
SET #Result = EncryptByKey(Key_GUID('My_Encryption_Symmetric_Key'), #ValueToEncrypt)
-- Return the result of the function
RETURN #Result
END
For decrypting:
CREATE FUNCTION Decrypt
(
#ValueToDecrypt varbinary(max)
)
RETURNS varchar(max)
AS
BEGIN
-- Declare the return variable here
DECLARE #Result varchar(max)
SET #Result = DecryptByKey(#ValueToDecrypt)
-- Return the result of the function
RETURN #Result
END
For inserting
exec sp_OpenEncryptionKeys
INSERT INTO tbl VALUES ('Name', Encrypt('some text here'))
For fetching
exec sp_OpenEncryptionKeys
SELECT ID, Decrypt(LongText) from tbl
Hope this helps someone.

Running into misbehaviour during hashing the password field

I want to store hashed passwords in my database and I have used the following code:
ALTER PROCEDURE AddUser
#name NVARCHAR(MAX),
#password NVARCHAR(MAX),
#responseMessage NVARCHAR(MAX) OUTPUT
AS
Begin
SET NOCOUNT ON
INSERT INTO [User] (Username, PasswordHashed)
VALUES (#name, HASHBYTES('SHA2_512', #password));
END
Current instance of my table has the following state (The User's password is hi):
For validating users I'm using the following code:
SELECT COUNT(*)
FROM [User]
WHERE [User].Username = 'Bamdad' AND [User].PasswordHashed = HASHBYTES('SHA2_512', 'hi');
But the result is 0. Why doesn't the latter code work?
You are specifying your password 'hi' as varchar but procedure requires an nvarchar. So the varchar gets promoted to nvarchar with an extra byte, hence the difference in encryption.

Store such characters in SQL Server 2008 R2

I'm storing encrypted passwords in the database, It worked perfect so far on MachineA. Now that I moved to MachineB it seems like the results gets corrupted in the table.
For example: ù9qÆæ\2 Ý-³Å¼]ó will change to ?9q??\2 ?-³?¼]? in the table.
That's the query I use:
ALTER PROC [Employees].[pRegister](#UserName NVARCHAR(50),#Password VARCHAR(150))
AS
BEGIN
DECLARE #Id UNIQUEIDENTIFIER
SET #Id = NEWID()
SET #password = HashBytes('MD5', #password + CONVERT(VARCHAR(50),#Id))
SELECT #Password
INSERT INTO Employees.Registry (Id,[Name],[Password]) VALUES (#Id, #UserName,#Password)
END
Collation: SQL_Latin1_General_CP1_CI_AS
ProductVersion: 10.50.1600.1
Thanks
You are mixing 2 datatypes:
password need to be nvarchar to support non-Western European characters
literals need N prefix
Demo:
DECLARE #pwdgood nvarchar(150), #pwdbad varchar(150)
SET #pwdgood = N'ù9qÆæ\2 Ý-³Å¼]ó'
SET #pwdbad = N'?9q??\2 ?-³?¼]?'
SELECT #pwdgood, #pwdbad
HashBytes gives varbinary(8000) so you need this in the table
Note: I'd also consider salting the stored password with something other than ID column for that row
If you want to store such characters, you need to:
use NVARCHAR as the datatype for your columns and parameters (#Password isn't NVARCHAR and the CAST you're using to assign the password in the database table isn't using NVARCHAR either, in your sample ...)
use the N'....' syntax for indicating Unicode string literals
With those two in place, you should absolutely be able to store and retrieve any valid Unicode character

Resources