Convert Aes.Key to SecureString in C# - c#

How do I convert Aes.Key to a secureString ? I am doing a byte[] -> string -> securestring. I am facing a different problem.
When converting the key, in byte[], to string and back to byte[] I get a different byte[]. What is the problem with the code ?
Aes aes = Aes.Create();
aes.GenerateIV();
aes.GenerateKey();
byte[] byteKey1 = aes.Key;
string sKey = Encoding.UniCode.GetString(byteKey);
byte[] byteKey2= Encoding.UniCode.GetBytes(sKey);
"byteKey1" and "byteKey2" are sometimes different. They are equal if I use Encoding.Default but that has problems when different machines have different default encoding.
How do I convert the Key in byte[] to SecureString and back to byte[] ?
Thanks.

Never use text encoding (e.g., Unicode or ASCII) on binary data like a cryptographic key or ciphertext. Encoding is intended for textual representations, and the implementation can change the binary contents as permitted by the encoding.
Instead, use Convert.ToBase64String and Convert.FromBase64String to convert binary text into a form that can be encoded in a textual format.
The following code will illustrate byteKey2 and byteKey will be identical.
string sKey = Convert.ToBase64String(byteKey);
byte[] byteKey2= Convert.FromBase64String(sKey);
bool equal = byteKey.SequenceEqual(byteKey2); // will be true

Related

How to properly hash a password

I want to re-create the MyBB hashing process so I can use its database to authenticate users on a 3rd party app (written on C#).
MyBB uses:
md5(md5($salt).password)
My problem is that the result I get on C# is nowhere similar to the one MyBB gets.
What I've done on C#:
public string HashPass(string password, string salt)
{
MD5 md5 = new MD5CryptoServiceProvider();
byte[] saltHash =md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(salt));
string passwordAndSalt = password + System.Text.Encoding.ASCII.GetString(saltHash);
byte[] finalHash = md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(passwordAndSalt));
string final = System.Text.Encoding.ASCII.GetString(finalHash);
return final;
}
The result I get from using that function for password "Test123" and salt "0fYR6mEE" (gathered from MyBB db) is: "??R?????s??" while the actual result should look like "VaHffsyzJeEa4dB3bbMWeUlJObAfN5I9rf1CuNRXCa6xPJTzXL".
Most likely I'm missing something obvious, sorry about that.
There are unknowns here. Which encoding does MyBB use to the password bytes? It could be ASCII, ANSI, or UTF8 or it could get the string bytes directly, i.e., without encoding. So I will write it partly as pseudo code
byte[] passwordBytes = GetBytes(password); // Where you have to define GetBytes
byte[] saltBytes = System.Convert.FromBase64String(salt); // Assuming it is given as base64
// Merge the password bytes and the salt bytes
var mergedBytes = new byte[passwordBytes.Length + saltBytes.Length];
Array.Copy(passwordBytes, mergedBytes, passwordBytes.Length);
Array.Copy(saltBytes, 0, mergedBytes, passwordBytes.Length, saltBytes.Length);
var md5 = new MD5CryptoServiceProvider();
byte[] finalHash = md5.ComputeHash(mergedBytes);
string final = System.Convert.ToBase64String(finalHash);
Note that I'm merging the password bytes and the salt bytes, not the password string and the salt string. Then the MD5 it taken only once from these merged bytes.
But I'm not sure what md5(md5($salt).password) does. Is md5() returning the the hash as base64 string already? Maybe you would have to convert the salt from base64 to bytes[], then get the MD% hash, convert it into a base64 string and then concatenate it with the password string. Then get the bytes from this combined string, do the hash again and convert the result to a base64 string again.
You would have to dig deeper into the source code of MyBB to be sure.

AES do I need to split my decoded message to chunks

I have a picture and I want to encrypt it using AES, do I need to split it to chunks? (because if I get a key with the size of 256 bits and my message is bigger, if I xor them, most of the message will remain the decoded message, so how will that help?)
if yes, how do I go about doing that?
AES is not XOR encrpytion and you do not need to block
// this could also just be a byte array
string original = "Here is some data to encrypt!";
using (Aes myAes = Aes.Create())
{
// Encrypt the string to an array of bytes.
byte[] encrypted = EncryptStringToBytes_Aes(original,
myAes.Key, myAes.IV);
// Decrypt the bytes to a string.
string roundtrip =
DecryptStringFromBytes_Aes(encrypted,
myAes.Key, myAes.IV);
}

SHA256 hash of String in C# does not agree with hash on website

If I hash the string "password" in C# using SHA256 using the below method I get this as my output:
e201065d0554652615c320c00a1d5bc8edca469d72c2790e24152d0c1e2b6189
But this website(SHA-256 produces a 256-bit (32-byte) hash value) tells me the has is:
5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
I'm clearly having a data format issue or something similar. Any ideas why this C# SHA256Managed method is returning something different? I'm sending the method "password" as the input parameter.
private static string CalculateSHA256Hash(string text)
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] hashValue;
byte[] message = UE.GetBytes(text);
SHA256Managed hashString = new SHA256Managed();
string hex = "";
hashValue = hashString.ComputeHash(message);
foreach (byte x in hashValue)
{
hex += String.Format("{0:x2}", x);
}
return hex;
}
UnicodeEncoding is UTF-16, guaranteed to inflate all your characters to two to four bytes to represent their Unicode code point (see also the Remarks section on MSDN).
Use (the static) Encoding.UTF8 instead, so most characters will fit in one byte. It depends on which system you need to mimic whether that will work for all strings.
After computing the hash using Encoding.UTF8 as stated on the answer above, I would also return a string as follows:
hex = BitConverter.ToString(hashValue).Replace("-", "");
return hex;

Error in converting from Encrypted value

I have a encryption method GetDecryptedSSN(). I tested it’s correctness by the following test. It works fine
//////////TEST 2//////////
byte[] encryptedByteWithIBMEncoding2 = DecryptionServiceHelper.GetEncryptedSSN("123456789");
string clearTextSSN2 = DecryptionServiceHelper.GetDecryptedSSN(encryptedByteWithIBMEncoding2);
But when I do a conversion to ASCII String and then back, it is not working correctly. What is the problem in the conversion logic?
//////////TEST 1//////////
//String -- > ASCII Byte --> IBM Byte -- > encryptedByteWithIBMEncoding
byte[] encryptedByteWithIBMEncoding = DecryptionServiceHelper.GetEncryptedSSN("123456789");
//encryptedByteWithIBMEncoding --> Encrypted Byte ASCII
string EncodingFormat = "IBM037";
byte[] encryptedByteWithASCIIEncoding = Encoding.Convert(Encoding.GetEncoding(EncodingFormat), Encoding.ASCII,
encryptedByteWithIBMEncoding);
//Encrypted Byte ASCII - ASCII Encoded string
string encodedEncryptedStringInASCII = System.Text.ASCIIEncoding.ASCII.GetString(encryptedByteWithASCIIEncoding);
//UpdateSSN(encodedEncryptedStringInASCII);
byte[] dataInBytesASCII = System.Text.ASCIIEncoding.ASCII.GetBytes(encodedEncryptedStringInASCII);
byte[] bytesInIBM = Encoding.Convert(Encoding.ASCII, Encoding.GetEncoding(EncodingFormat),
dataInBytesASCII);
string clearTextSSN = DecryptionServiceHelper.GetDecryptedSSN(bytesInIBM);
Helper Class
public static class DecryptionServiceHelper
{
public const string EncodingFormat = "IBM037";
public const string SSNPrefix = "0000000";
public const string Encryption = "E";
public const string Decryption = "D";
public static byte[] GetEncryptedSSN(string clearTextSSN)
{
return GetEncryptedID(SSNPrefix + clearTextSSN);
}
public static string GetDecryptedSSN(byte[] encryptedSSN)
{
return GetDecryptedID(encryptedSSN);
}
private static byte[] GetEncryptedID(string id)
{
ServiceProgram input = new ServiceProgram();
input.RequestText = Encodeto64(id);
input.RequestType = Encryption;
ProgramInterface inputRequest = new ProgramInterface();
inputRequest.Test__Request = input;
using (MY_Service operation = new MY_Service())
{
return ((operation.MY_Operation(inputRequest)).Test__Response.ResponseText);
}
}
private static string GetDecryptedID(byte[] id)
{
ServiceProgram input = new ServiceProgram();
input.RequestText = id;
input.RequestType = Decryption;
ProgramInterface request = new ProgramInterface();
request.Test__Request = input;
using (MY_Service operationD = new MY_Service())
{
ProgramInterface1 response = operationD.MY_Operation(request);
byte[] encodedBytes = Encoding.Convert(Encoding.GetEncoding(EncodingFormat), Encoding.ASCII,
response.Test__Response.ResponseText);
return System.Text.ASCIIEncoding.ASCII.GetString(encodedBytes);
}
}
private static byte[] Encodeto64(string toEncode)
{
byte[] dataInBytes = System.Text.ASCIIEncoding.ASCII.GetBytes(toEncode);
Encoding encoding = Encoding.GetEncoding(EncodingFormat);
return Encoding.Convert(Encoding.ASCII, encoding, dataInBytes);
}
}
REFERENCE:
Getting incorrect decryption value using AesCryptoServiceProvider
This is the problem, I suspect:
string encodedEncryptedStringInASCII =
System.Text.ASCIIEncoding.ASCII.GetString(encryptedByteWithASCIIEncoding);
(It's not entirely clear because of all the messing around with encodings beforehand, which seems pointless to me, but...)
The result of encryption is not "text encoded in ASCII" - so you shouldn't try to treat it that way. (You haven't said what kind of encryption you're using, but it would be very odd for it to produce ASCII text.)
It's just an arbitrary byte array. In order to represent that in text only using the ASCII character set, the most common approach is to use base64. So the above code would become:
string encryptedText = Convert.ToBase64(encryptedByteWithIBMEncoding);
Then later, you'd convert it back to a byte array ready for decryption as:
encryptedByteWithIBMEncoding = Convert.FromBase64String(encryptedText);
I would strongly advise you to avoid messing around with the encodings like this if you can help it though. It's not clear why ASCII needs to get involved at all. If you really want to encode your original text as IBM037 before encryption, you should just use:
Encoding encoding = Encoding.GetEncoding("IBM037");
string unencryptedBinary = encoding.GetBytes(textInput);
Personally I'd usually use UTF-8, as an encoding which can handle any character data rather than just a limited subset, but that's up to you. I think you're making the whole thing much more complicated than it needs to be though.
A typical "encrypt a string, getting a string result" workflow is:
Convert input text to bytes using UTF-8. The result is a byte array.
Encrypt result of step 1. The result is a byte array.
Convert result of step 2 into base64. The result is a string.
To decrypt:
Convert the string from base64. The result is a byte array.
Decrypt the result of step 1. The result is a byte array.
Convert the result of step 2 back to a string using the same encoding as step 1 of the encryption process.
In DecryptionServiceHelper.GetEncryptedSSN you are encoding the text in IBM037 format BEFORE encrypting.
So the following piece of code is not correct as you are converting the encrypted bytes to ASCII assuming that its in the IBM037 format. That's wrong as the encrypted bytes is not in IBM037 format (the text was encoded before encryption)
//encryptedByteWithIBMEncoding --> Encrypted Byte ASCII
string EncodingFormat = "IBM037";
byte[] encryptedByteWithASCIIEncoding = Encoding.Convert(Encoding.GetEncoding(EncodingFormat), Encoding.ASCII,
encryptedByteWithIBMEncoding);
One possible solution is to encode the encrypted text using IBM037 format, that should fix the issue I guess.

Why isn't my PHP SHA256 hash equivalent to C# SHA256Managed hash

Why aren't these the same?
php:
$hash = hash('sha256', $userData['salt'] . hash('sha256', $password) );
c#
public static string ComputeHash(string plainText, string salt)
{
// Convert plain text into a byte array.
byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
byte[] saltBytes = Encoding.UTF8.GetBytes(salt);
SHA256Managed hash = new SHA256Managed();
// Compute hash value of salt.
byte[] plainHash = hash.ComputeHash(plainTextBytes);
byte[] concat = new byte[plainHash.Length + saltBytes.Length];
System.Buffer.BlockCopy(saltBytes, 0, concat, 0, saltBytes.Length);
System.Buffer.BlockCopy(plainHash, 0, concat, saltBytes.Length, plainHash.Length);
byte[] tHashBytes = hash.ComputeHash(concat);
// Convert result into a base64-encoded string.
string hashValue = Convert.ToBase64String(tHashBytes);
// Return the result.
return hashValue;
}
C# is outputting a base64 ecoded string, and PHP is outputting a number in hex. A better comparison might be to pass the parameter true to the end of the hash function of PHP and base64 the result:
$hash = base64_encode(
hash('sha256', $userData['salt'] . hash('sha256', $password), true )
);
Because they're different. Your C# code encodes the computed hash in Base64 encoding at the end. PHP just returns a string of hexadecimal digits.
First suspect:
Encoding.UTF8.GetBytes(plainText);
C# uses UTF-8, your PHP probably doesn't, but you could be lucky if you use strictly letters from the US-ASCII subset.
Second suspect:
Convert.ToBase64String(tHashBytes);
There's nothing about Base64 in your PHP.
Since PHP will give you a hex-encoded result, you should switch to Hex in your C#, too. See this answer for solutions.
Well I'm no C# programmer, but one thing that leaps out at me is this:
// Convert result into a base64-encoded string.
string hashValue = Convert.ToBase64String(tHashBytes);
Are you base64-encoding the final output in C#? Because you're not in PHP...
C#
string toHash = "abcdefg";
SHA256Managed hash = new SHA256Managed();
byte[] signatureData = hash.ComputeHash(new UnicodeEncoding().GetBytes(toHash));
string hashResult = System.Convert.ToBase64String(signatureData);
PHP
print base64_encode(hash("sha256",mb_convert_encoding("abcdefg","UTF-16LE"),true));
Write like top code,They are the same
C#
string toHash = "abcdefg";
SHA256Managed hash = new SHA256Managed();
byte[] signatureData = hash.ComputeHash(new UnicodeEncoding().GetBytes(toHash));
string hashResult = System.Convert.ToBase64String(signatureData);
PHP
print base64_encode(hash("sha256",mb_convert_encoding("abcdefg","UTF-16LE"),true));
Works for me.

Categories