SHA2_512 encryption c# and SQL methods gives different results - c#

I have intercepted the word "admin" using this c# code
Byte[] inputBytes = Encoding.UTF8.GetBytes(stringpassword);
SHA512 shaM = new SHA512Managed();
Byte[] hashedBytes = shaM.ComputeHash(inputBytes);
string hashedpassword = BitConverter.ToString(hashedBytes);
and got the result of this "DA-EF-49-53-B9-78-33-65-CA-D6-61-52-23-72-05-06-CC". and I encrypyt the same word "admin" using SQL stored procedure
SET #password = HASHBYTES('SHA2_512',#password);
and get this as output "Ç­DË­v*] ¤RùèTýÁàç¥*8_#óê±Ø“ÔrcMúÇÓN¼5Ñj·ûŠÈ"
Why is there difference between these to methods?

From the documentation of the HASHBYTES function:
Return Value
varbinary (maximum 8000 bytes)
The issue here is that you're trying to interpret arbitrary binary data (the output of HASHBYTES) as a textual value, which just won't work. In this case SQL server is trying to interpret the raw byte values as characters in whatever collation your database is using.
The standard way of representing binary data in a textual form is to convert it to a base64 representation. To do this in C# replace the last line with:
string hashedpassword = Convert.ToBase64String(hashedBytes);
Then in your SQL you can do the following to convert your hashed value to base64 (based on this SO answer):
DECLARE #hashedValue VARBINARY(8000) = HASHBYTES('SHA2_512', 'admin')
SELECT
CAST(N'' AS XML).value(
'xs:base64Binary(xs:hexBinary(sql:column("bin")))'
, 'VARCHAR(MAX)'
) Base64Encoding
FROM (
SELECT #hashedValue AS bin
) AS bin_sql_server_temp;
If you run this you will observe that the base64 encoded values are identical.

Related

Using ExecuteSqlCommand makes my password not work when inserting to DB

I'm using MySql DB and trying to create a user there. The password is a hash.
When I use the Context.Useraccount.add(User) and Context.SaveChanges() adding to the database works just fine, but using ExecuteSqlCommmand makes the password not work.
var sql = #"INSERT INTO useraccount
(UserId,UserName,Password,CustomerId,PasswordSalt,CreatedDate)
VALUES
(#UserId, #UserName,#Password,#CustomerId, #PasswordSalt, #CreatedDate)";
int rows = _context.Database.ExecuteSqlCommand(
sql,
new MySqlParameter("#UserId", user.UserId),
new MySqlParameter("#UserName", user.UserName),
new MySqlParameter("#Password", user.Password),
new MySqlParameter("#CustomerId", user.CustomerId),
new MySqlParameter("#PasswordSalt", user.PasswordSalt),
new MySqlParameter("#CreatedDate", MySQLFormatDate));
It gives this exception: {MySql.Data.MySqlClient.MySqlException (0x80004005): Incorrect string value: '\x90]\x0E\x80\xB1\xFF...' for column 'Password' at row 1 ---> MySql.Data.MySqlClient.MySqlException (0x80004005): Incorrect string value: '\x90]\x0E\x80\xB1\xFF...' for column 'Password' at row 1
I tried changing the Column value in the DB to varbinary (from varchar) and then I can insert it, but it becomes a blob in the DB and it doesn't work when I try to read it again.
How can I send the hash correctly to the DB?
Edit---
code for creating the hash
private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
{
if (password == null) throw new ArgumentNullException("password");
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
using (var hmac = new System.Security.Cryptography.HMACSHA512())
{
passwordSalt = hmac.Key;
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
}
}
edit2---
password types are byte[]
This is if I change the Password and SaltedPassword types in the database to VarBinary. When I use VarChar it gives the error which I pasted before and nothing gets sent to the DB.
Text Image
UPDATE -- SOLVED
So I had the wrong value in the modelbuilder.entity for the password I had VarChar there when it should've been VarBinary.
Somehow it worked with Context.Useraccount.add(User) and Context.SaveChanges() though.
Thanks to everyone for the help!
Password hashes are byte arrays, and cannot be stored in a C# string. They also have to be stored in a BINARY (or VARBINARY) column in the database, not a VARCHAR column.
but it becomes a blob in the DB and it doesn't work when I try to read it again
To verify the user's password, you should read back the password salt (as a byte[]), hash the (plain text) password with the same salt, then compare the newly-generated hash to the password hash retrieved from the database (as a byte[]). Do not try to convert the password hash back to a C# string.
Paraphrasing the answer from this answer since it applies to your situation as well
"Incorrect string value" when trying to insert UTF-8 into MySQL via JDBC?
MySql's utf8 encoding only supports characters that can be encoded in 3 Bytes. Whatever character '\x90]\x0E\x80\xB1\xFF' is probably takes more than 3 bytes to encode and that is why MySql is yelling at you.
Verify that whatever method you are using to encode these passwords is limited to a utf8 format and that should prevent this error from re-occuring.
I can't see your method for hashing these, but this will ensure you are encoding using utf8
byte[] salt = // Salt
byte[] encodedValue = Encoding.UTF8.GetBytes(value);
byte[] saltedValue = value.Concat(salt).ToArray();
byte[] hashedValue = SHA256Managed().ComputeHash(saltedValue);

Storing result convert.FromBase64String into postgres bytea

I have a mini system where frontend will pass the file in base64 format to the backend. I used convert.frombase64string in order for me to convert the base64 format into byte array and used file stream to save the file into the server.
The codes are shown as below:
byte[] bytes = Convert.FromBase64String(file.Split(',')[1]);
using (var file = new FileStream("D:test.txt", FileMode.Create))
{
file.Write(bytes, 0, bytes.Length);
file.Flush();
}
var db = await _context.insertDB.FromSql("INSERT INTO blobTable (blob) VALUES ('" + bytes + "')").SingleAsync();
And this is the result that I select from blobTable.
As the return result from Convert.FromBase64String() is byte array. So that I decided to store this value into my database which is postgres with column bytea[].
The problem is that is it so weird that when I trying to
console.writeline(bytes);
The result is printed as "system.byte[]" instead of the bytes value. So that "system.byte[]" is stored into my database instead of the actual value.
Can anybody tell me how do I store the return value from convert.frombase64string() into postgres bytea[] column? Thanks you.

How to store UTF-8 bytes from a C# String in a SQL Server 2000 TEXT column

I have an existing SQL Server 2000 database that stores UTF-8 representations of text in a TEXT column. I don't have the option of modifying the type of the column, and must be able to store non-ASCII Unicode data from a C# program into that column.
Here's the code:
sqlcmd.CommandText =
"INSERT INTO Notes " +
"(UserID, LocationID, Note) " +
"VALUES (" +
Note.UserId.ToString() + ", " +
Note.LocationID.ToString() + ", " +
"#note); " +
"SELECT CAST(SCOPE_IDENTITY() AS BIGINT) ";
SqlParameter noteparam = new SqlParameter( "#note", System.Data.SqlDbType.Text, int.MaxValue );
At this point I've tried a few different ways to get my UTF-8 data into the parameter. For example:
// METHOD ONE
byte[] bytes = (byte[]) Encoding.UTF8.GetBytes( Note.Note );
char[] characters = bytes.Select( b => (char) b ).ToArray();
noteparam.Value = new String( characters );
I've also tried simply
// METHOD TWO
noteparam.Value = Note.Note;
And
// METHOD THREE
byte[] bytes = (byte[]) Encoding.UTF8.GetBytes( Note.Note );
noteparam.Value = bytes;
Continuing, here's the rest of the code:
sqlcmd.Parameters.Add( noteparam );
sqlcmd.Prepare();
try
{
Note.RecordId = (Int64) sqlcmd.ExecuteScalar();
}
catch
{
return false;
}
Method one (get UTF8 bytes into a string) does something strange -- I think it is UTF-8 encoding the string a second time.
Method two stores garbage.
Method three throws an exception in ExecuteScalar() claiming it can't convert the parameter to a String.
Things I already know, so no need telling me:
SQL Server 2000 is past/approaching end-of-life
TEXT columns are not meant for Unicode text
Seriously, SQL Server 2000 is old. You need to upgrade.
Any suggestions?
If your database collation is SQL_Latin1_General_CP1 (the default for the U.S. edition of SQL Server 2000), then you can use the following trick to store Unicode text as UTF-8 in a char, varchar, or text column:
byte[] bytes = Encoding.UTF8.GetBytes(Note.Note);
noteparam.Value = Encoding.GetEncoding(1252).GetString(bytes);
Later, when you want to read back the text, reverse the process:
SqlDataReader reader;
// ...
byte[] bytes = Encoding.GetEncoding(1252).GetBytes((string)reader["Note"]);
string note = Encoding.UTF8.GetString(bytes);
If your database collation is not SQL_Latin1_General_CP1, then you will need to replace 1252 with the correct code page.
Note: If you look at the stored text in Enterprise Manager or Query Analyzer, you'll see strange characters in place of non-ASCII text, just as if you opened a UTF-8 document in a text editor that didn't support Unicode.
How it works: When storing Unicode text in a non-Unicode column, SQL Server automatically converts the text from Unicode to the code page specified by the database collation. Any Unicode characters that don't exist in the target code page will be irreversibly mangled, which is why your first two methods didn't work.
But you were on the right track with method one. The missing step is to "protect" the raw UTF-8 bytes by converting them to Unicode using the Windows-1252 code page. Now, when SQL Server performs the automatic conversion from Unicode to Windows-1252, it gets back the original UTF-8 bytes untouched.

Invalid length for a Base-64 char array

I'm getting a "Invalid length for a Base-64 char array." inside of the IF(){...} are variations i have tried to get it to work. it fails in the first line without calling decrypt(...) proving it's not that functions problem. i get the same error inside with the first decrypt(...) call. the last one using the encoding.ascii... will get me inside the function, but then it fails inside the function. I'm getting the proper encrypted info from the database to string SSnum. it's value is: 4+mFeTp3tPF
try
{
string SSnum = dr.GetString(dr.GetOrdinal("Social Security"));
if (isEncrypted)
{
byte[] temp = Convert.FromBase64String(SSnum);
//SSnum = decrypt(Convert.FromBase64String(SSnum), Key, IV);
//SSnum = decrypt(Encoding.ASCII.GetBytes(SSnum), Key, IV);
}
txt_Social_Security.Text = SSnum;
}
catch { txt_Social_Security.Text = ""; }
I've been told to use the Convert.FromBase64String() and not the ASCII method...so why is it failing, how can i fix it?
Base64 data length should be multiple of 4 and with padding char '='
You can change your data as valid base64 data.
string dummyData = imgData.Trim().Replace(" ", "+");
if (dummyData.Length % 4 > 0)
dummyData = dummyData.PadRight(dummyData.Length + 4 - dummyData.Length % 4, '=');
byte[] byteArray = Convert.FromBase64String(dummyData);
https://stackoverflow.com/a/9301545/2024022
This will help you , try once.
Thanks
suribabu.
it's value is: 4+mFeTp3tPF
You are receiving this error because that value, 4+mFeTp3tPF, is in fact not valid Base64.
Is it possible you are simply missing the required padding character, as so 4+mFeTp3tPF=?
Are you certain that you have a Base64 string? Base64 is a means of encoding binary data into a string while only using standard 7-bit ASCII characters. It's not a string encoding like ASCII and has some control bytes present. You have a Base64 string if you're using Convert.ToBase64String to obtain the value (which, if you're trying to store binary data as a string, is your best bet)
Judging by your error (and your example data), I'm assuming that you do not have a Base64 string. If you need to store binary data in the database, you can either create a column using a binary type or encode the string into Base64 by using Convert.ToBase64String.
byte[] inputData = ...;
string base64String = Convert.ToBase64String(inputData);
byte[] outputData = Convert.FromBase64String(base64String);
Here, outputData should contain the same data as inputData.
If what you have is just an ASCII-encoded string, then your original practice of using System.Text.Encoding.ASCII.GetBytes() is correct, but you should change this to use a Base64 string if you can.
Are you sure that string 4+mFeTp3tPF is well-formed Base64 string?
I've tried some online services - no one could convert it.
replace
byte[] temp = Convert.FromBase64String(SSnum);
to this
var temp = UTF8Encoding.UTF8.GetBytes(SSnum);

Best way to store hashed passwords and salt values in the database - varchar or binary?

Once I've generated a salt and hashed the password (using bcrypt, etc) would it be best to store the results as a string or as a byte array in the database? Are there any benefits either way? Or is it a more subjective decision?
If you use VARCHAR either make sure the bytes are either valid characters or force the bytes to ASCII before you save the hash and salt. To make is safer from data corruption you may want to either encode the bytes in Base64 or Hexadecimal before you store them in the VARCHAR field.
For example, if you store the byte[] output from MD5 or SHA1 some of the byte values may be removed or replaced when the byte[] is converted to text is they are not valid UTF-8/Unicode. This should not be as much of a problem if you are using ASCII but it could still cause data corruption.
If you don't want to deal with Hex you could use this base64 method Convert.ToBase64String that is built into the framework.
If you use VARBINARY and don't try to use and text encoding methods this issue should not exist, but could make comparing hashes more difficult.
...Note that if you use NVARCHAR this data corruption could still occur...
static void Main(string[] args)
{
var salt = "MySalt";
var password = "MyPassword";
var saltedKey = CalculateHash(salt, password);
Console.WriteLine(saltedKey);
// MySalt$teGOpFi57nENIRifSW3m1RQndiU=
var checkHash = CheckHash(saltedKey, password);
Console.WriteLine(checkHash);
// True
}
private static string CalculateHash(string saltOrSaltedKey, string password)
{
var salt =
saltOrSaltedKey.Contains('$')
? saltOrSaltedKey.Substring(0, saltOrSaltedKey.IndexOf('$') + 1)
: saltOrSaltedKey + '$';
var newKey = Encoding.UTF8.GetBytes(salt + password);
var sha1 = SHA1.Create();
sha1.Initialize();
var result = sha1.ComputeHash(newKey);
// if you replace this base64 version with one of the encoding
// classes this will become corrupt due to nulls and other
// control character values in the byte[]
var outval = salt + Convert.ToBase64String(result);
return outval;
}
private static bool CheckHash(string saltedKey, string password)
{
var outval = CalculateHash(saltedKey, password);
return outval == saltedKey;
}
VARCHAR should be sufficient. In my opinion VARCHAR data will be easier to consume and work with vs. binary.
The built in ASP.NET membership providers also store hashed passwords as VARCHAR fields.
most md5/sha1 libraries return hashes base64 encoded in which case varchar would suffice

Categories