C# & Lua MD5 Hashing - c#

I am having some issues trying to match an MD5 encryption with salt from Lua to C#. Meaning, I have it hashing and salting the account password in Lua, but I need to match that exact same encryption in C# as I am developing a website in C# that needs to use the same database and accounts as the Lua script.
I've tried for quite some time now trying to match them, but no matter what I do I can't seem to get it right.
Lua hash:
if (string.len(cpypassword) ~= 64) then
password = md5(Newsalt .. password)
local result = mysql:query("SELECT username FROM accounts WHERE username='" .. username .. "'")
if (mysql:num_rows(result)>0) then
local insertid = mysql:query_insert_free("UPDATE accounts SET password='" .. mysql:escape_string(password) .. "' WHERE username='".. mysql:escape_string(username) .."'")
triggerClientEvent(client, "accounts:login:attempt", client, 1, "Password changed!\nThank you." )
end
end
I've tried a variety of different ways to do MD5 hash in C#, but none of them matches, so here I am now, asking you for suggestions.
Thank you in advance.
EDIT:
Lua function generates this as an example:
CFA62AA942A84781B1C101D6D583B641
Same example generated in C# with the C# hashing:
DSqwG/W1LNbHsCEkHNAUpg==
C# code (just one of the things I tried, I found much simpler ones, but this is the latest one I tried, just copied out of a tutorial)
public class Encryption
{
public static string EncryptorDecrypt(string securityCode, string key, bool encrypt)
{
byte[] toEncryptorDecryptArray;
ICryptoTransform cTransform;
// Transform the specified region of bytes array to resultArray
MD5CryptoServiceProvider md5Hasing = new MD5CryptoServiceProvider();
byte[] keyArrays = md5Hasing.ComputeHash(Encoding.UTF8.GetBytes(securityCode));
md5Hasing.Clear();
TripleDESCryptoServiceProvider tdes = new TripleDESCryptoServiceProvider() { Key = keyArrays, Mode = CipherMode.ECB, Padding = PaddingMode.PKCS7 };
if (encrypt == true)
{
toEncryptorDecryptArray = Encoding.UTF8.GetBytes(key);
cTransform = tdes.CreateEncryptor();
}
else
{
toEncryptorDecryptArray = Convert.FromBase64String(key.Replace(' ', '+'));
cTransform = tdes.CreateDecryptor();
}
byte[] resultsArray = cTransform.TransformFinalBlock(toEncryptorDecryptArray, 0, toEncryptorDecryptArray.Length);
tdes.Clear();
if (encrypt)
{ //if encrypt we need to return encrypted string
return Convert.ToBase64String(resultsArray, 0, resultsArray.Length);
}
//else we need to return decrypted string
return Encoding.UTF8.GetString(resultsArray);
}
}

The code you provided for C# is not generating an MD5 hash; instead it is hashing the securityCode and using it as a key for TripleDES.
Take a look at this blog post (copied relevant bits out):
public string CalculateMD5Hash(string input)
{
// step 1, calculate MD5 hash from input
MD5 md5 = System.Security.Cryptography.MD5.Create();
byte[] inputBytes = System.Text.Encoding.ASCII.GetBytes(input);
byte[] hash = md5.ComputeHash(inputBytes);
// step 2, convert byte array to hex string
StringBuilder sb = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sb.Append(hash[i].ToString("X2"));
}
return sb.ToString();
}

Related

CryptoJS AES encryption with MD5 and SHA256 in C# not generated proper value

I want to encrypt password using both in CryptoJS and C#. Unfortunately, my C# code fails to generate the proper value. This is my code
internal static byte[] ComputeSha256(this byte[] value)
{
using (SHA256 sha256Hash = SHA256.Create())
return sha256Hash.ComputeHash(value);
}
internal static byte[] ComputeSha256(this string value) => ComputeSha256(Encoding.UTF8.GetBytes(value));
internal static byte[] ComputeMD5(this byte[] value)
{
using (MD5 md5 = MD5.Create())
return md5.ComputeHash(value);
}
internal static byte[] ComputeMD5(this string value) => ComputeMD5(Encoding.UTF8.GetBytes(value));
internal static byte[] CombineByteArray(byte[] first, byte[] second)
{
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
internal static string EncryptPassword()
{
using (AesManaged aes = new AesManaged())
{
//CLIENT SIDE PASSWORD HASH
/*
var password = '12345';
var passwordMd5 = CryptoJS.MD5(password);
var passwordKey = CryptoJS.SHA256(CryptoJS.SHA256(passwordMd5 + '12345678') + '01234567890123456');
var encryptedPassword = CryptoJS.AES.encrypt(passwordMd5, passwordKey, { mode: CryptoJS.mode.ECB, padding: CryptoJS.pad.NoPadding });
encryptedPassword = CryptoJS.enc.Base64.parse(encryptedPassword.toString()).toString(CryptoJS.enc.Hex);
//encryptedPassword result is c3de82e9e8a28a4caded8c2ef0d49c80
*/
var y1 = Encoding.UTF8.GetBytes("12345678");
var y2 = Encoding.UTF8.GetBytes("01234567890123456");
var password = "12345";
var passwordMd5 = ComputeMD5(password);
var xkey = CombineByteArray(ComputeSha256(CombineByteArray(passwordMd5, y1)), y2);
var passwordKey = ComputeSha256(xkey);
aes.Key = passwordKey;
aes.Mode = CipherMode.ECB;
aes.Padding = PaddingMode.None;
ICryptoTransform crypt = aes.CreateEncryptor();
byte[] cipher = crypt.TransformFinalBlock(passwordMd5, 0, passwordMd5.Length);
var encryptedPassword = BitConverter.ToString(cipher).Replace("-", "").ToLower();
return encryptedPassword; //e969b60e87339625c32f805f17e6f993
}
}
The result of the C# code above is e969b60e87339625c32f805f17e6f993. It should be the same with CryptoJS c3de82e9e8a28a4caded8c2ef0d49c80. What is wrong here?
In the CryptoJS code hashes (in the form of WordArrays) and strings are added in several places. Thereby the WordArray is implicitly encoded with toString() into a hex string with lowercase letters. This is missing in the C# code.
In the C# code the addition is done with CombineByteArray(), where the hash is passed in the parameter first as byte[]. Therefore this parameter must first be converted to a hex encoded string with lowercase letters and then UTF8 encoded, e.g.:
internal static byte[] CombineByteArray(byte[] first, byte[] second)
{
// Hex encode (with lowercase letters) and Utf8 encode
string hex = ByteArrayToString(first).ToLower();
first = Encoding.UTF8.GetBytes(hex);
byte[] bytes = new byte[first.Length + second.Length];
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
return bytes;
}
where ByteArrayToString() is from here.
With this change, the C# code gives the same result as the CryptoJS code.
I am not quite clear about the purpose of the CryptoJS code. Usually plaintext and key are independent, i.e. are not derived from the same password.
Perhaps this is supposed to implement a custom password-based key derivation function. If so, and unless a custom implementation is mandatory for compatibility reasons, it is more secure to use a proven algorithm such as Argon2 or PBKDF2. In particular, the lack of a salt/work factor is insecure.

Need help creating IsValidPassword for PBKDF2 in C#

Having the hardest time trying to create a PBKDF2 valid password checker. The PBKDF2 code comes from a SharpHash project; https://github.com/ron4fun/SharpHash. The Class is: SharpHash/SharpHash.Tests/KDF/PBKDF2_HMACTests.cs
The example shows how to implement it but does not have any examples on how to verify the hash afterwards. I managed to tried several different "IsValidPassword" is one of the methods, but none of them seem to work. Each and every one of them the result is false no matter what values I add to the PBKDF2 or IsValidPassword methods. I also tried changing to a hex and also base64 but got the same results; it failed.
I even replaced Rfc2898DeriveBytes.
Does anyone have any experience with PBKDF2 password verification. This would be application based, not website based. IDE environment Visual Studios 2019 - C#.
Thank you.
public void TestOne()
{
IPBKDF2_HMAC PBKDF2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash, Password, Salt, 100000);
byte[] Key = PBKDF2.GetBytes(64);
PBKDF2.Clear();
string ActualString = Converters.ConvertBytesToHexString(Key, false);
Assert.AreEqual(ExpectedString, ActualString);
}
public bool IsValidPassword(string password, string hashPass)
{
bool result = false;
// Extract the bytes
byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
// Get the salt
byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
Array.Copy(hashBytes, 0, salt, 0, 20);// Doesn't matter what values and here; same issue… False
// Compute the hash on the password the user entered
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
byte[] hash = pbkdf2.GetBytes(64);
// compare the results
for (int i = 0; i < 20; i++) // If I go to 64 I get an error
{
if (hashBytes[i + 20] != hash[i])
{
return false;
}
}
return true;
}
// Replaced Rfc2898DeriveBytes
public bool IsValidPassword(string password, string hashPass)
{
bool result = false;
IHash hash1 = HashFactory.Crypto.CreateSHA1();
// Extract the bytes
byte[] hashBytes = Encoding.ASCII.GetBytes(hashPass);
byte[] Password = Encoding.ASCII.GetBytes(password);
// Get the salt
byte[] salt = new byte[20]; // Doesn't matter what values and here; same issue… False
Array.Copy(hashBytes, 0, salt, 0, 20); // Doesn't matter what values and here; same issue… False
// Compute the hash on the password the user entered
var pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, 100000); // Replaced Rfc2898DeriveBytes
byte[] Key = pbkdf2.GetBytes(64);
pbkdf2.Clear();
string test = Converters.ConvertBytesToHexString(Key, false); // Taking a peek
string test2 = Encoding.ASCII.GetString(hashBytes); // Taking a peek
// compare the results
for (int i = 0; i < 20; i++)
{
if (hashBytes[i + 20] != Key[i])
{
return false;
}
}
return true;
}
taking a quick look at this, the issue you are encountering is simply a misrepresentation of the data contents involved.
password is a base256 based data while hashPass is a base16 based data.
You have to use the appropriate conversion routine when converting hashPass to byte[].
To do that, simply use this method found in the Converters class too.
byte[] hashBytes = Converters.ConvertHexStringToBytes(hashPass);
Do note that I assumed that your password is in base256 (since you didn't specify this in the question) so you can leave it as it is.
The only change you need to make is the one I described above.
Well, I suppose this will help someone else down the road apiece. I had to do some serious searching and banging my head up against the wall because this was my first attempt at PBKDF2 to come up with a functional verification method that isn't included with the SharpHash project. Just so that anyone reading this would understand what my problem was. The code generated the password with no issues whatsoever. However, there was no function in the code project that actually verified the password using the salt, generated password and iteration.
The code provided is the simple version because I have also added overloads to the methods that I don't need to post. This method has default settings whereas one of the overloads allows for complete customization of the hash algorithm, salt and iteration. Each of these I had tested and they worked as expected.
Hope this helps someone. :-)
private static Int32 Salt256bit { get; } = 256 / 8; // 256 bits = 32 Bytes
public static string GetHashPBKDF2Password(string password)
{
// Notes:
// Create a 32-byte secure salt and the same size of the key. This 32-byte produces 256 bits key output.
// Add the same 32-byte size to the pbkdf2.GetBytes.
// KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC hashes these values using the crypto SHA presented.
// Double the hashBytes bytes then add the salt and pbkdf2.GetBytes value.
// Copy each of the 32-bytes to the hashBytes bytes from the hashed salt and hashed value.
//
byte[] Password = Encoding.Unicode.GetBytes(password);
string hashPass = string.Empty;
// Create the salt value with a cryptographic PRNG
byte[] salt;
new RNGCryptoServiceProvider().GetBytes(salt = new byte[ByteSize]); // 32 Bytes = 256 bits.
// Create the KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC and get the hash value using the SHA presented.
// I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
IHash sha = HashFactory.Crypto.CreateSHA1();
IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(sha, Password, salt, Pbkdf2Iterations);
byte[] hash = pbkdf2.GetBytes(ByteSize); // 32 Bytes = 256 bits.
// Double the size of the byte array to include the "pbkdf2.GetBytes" and salt.
Int32 g = hash.Length + salt.Length;
byte[] hashBytes = new byte[g];
// Combine the salt and password bytes.
Array.Copy(salt, 0, hashBytes, 0, ByteSize);
Array.Copy(hash, 0, hashBytes, ByteSize, ByteSize);
// Turn the combined salt+hash into a string for storage
hashPass = Convert.ToBase64String(hashBytes);
return hashPass;
}
public static bool ValidatePBKDF2Password(string password, string hashPass)
{
try
{
byte[] Password = Encoding.Unicode.GetBytes(password);
bool result = true;
// Extract the bytes
byte[] hashBytes = Convert.FromBase64String(hashPass);
// Get the salt
byte[] salt = new byte[32];
Array.Copy(hashBytes, 0, salt, 0, 32);
// Compute the hash on the password that user entered.
// I know SHA1 is not that secured at all anymore. Just using it to test with. :-)
IHash hash1 = HashFactory.Crypto.CreateSHA1();
IPBKDF2_HMAC pbkdf2 = HashFactory.KDF.PBKDF2_HMAC.CreatePBKDF2_HMAC(hash1, Password, salt, Pbkdf2Iterations);
byte[] hash = pbkdf2.GetBytes(32);
// compare the results
for (int i = 0; i < 32; i++)
{
if (hashBytes[i + 32] != hash[i])
{
result = false;
}
}
return result;
}
catch (Exception)
{
return false;
}
}
How to use: string GeneratedHash = PBKDF2Helper.GetHashPBKDF2Password("password");
Results: hv6t8N4rrVSKYFm80cCoVUEiUk2o11xLBc6lJb5kBXKTcwcKwl9dZwSdce01X0bi8BBhJY/QGGnNVAcR7ZhSvQ==
Verify Paword: Boolean tester = PBKDF2Helper.ValidatePBKDF2Password("password", GeneratedHash);
txtVerificationResults.Text = tester.ToString();

Creating custom length password in Rijndael Cryptography Algorithm

I'm using c# for implementing Rijndael algorithm to encrypt/decrypt files. Below is my code:
private void EncryptFile(string inputFile, string outputFile, string password)
{
try
{
UnicodeEncoding UE = new UnicodeEncoding();
byte[] key = UE.GetBytes(password.ToString());
string cryptFile = outputFile;
FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create);
RijndaelManaged RMCrypto = new RijndaelManaged();
CryptoStream cs = new CryptoStream(fsCrypt,
RMCrypto.CreateEncryptor(key, key),
CryptoStreamMode.Write);
FileStream fsIn = new FileStream(inputFile, FileMode.Open);
int data;
while ((data = fsIn.ReadByte()) != -1)
cs.WriteByte((byte)data);
fsIn.Close();
cs.Close();
fsCrypt.Close();
}
catch
{
}
}
Now, the thing is that, the function works only if password length is a multiple of 8. that is, if the password length is 8,16,32, etc., then it works else not.
Simply taking the password and getting its Unicode representation in bytes makes pretty terrible key. Please don't do that! The correct way to go is to use a salted hash as a key -- that is, take a salt, a password, and mix them together with a hash function.
To derive a key from a variable-length password, use PBKDF2. PBKDF2 is designed to make brute forcing slower when the attacker has fast access to the data.
string password = ...;
byte[] salt = ...;
int keyLength = 32;
byte[] key;
using(var pbkdf = new Rfc2898DeriveBytes(password, salt))
{
key = pbkdf.GetBytes(keyLength);
}
If you need something which uses less CPU, HMAC will work but also be faster to brute force:
using(var hmac = new HMACSHA256())
{
hmac.Key = salt;
key = hmac.ComputeHash(Encoding.UTF8.GetBytes(password));
}
Note that there exists hardware now which can very effectively attack PBKDF2 so this won't do much to help against a determined attacker with resources. If this is important to you, branching out of the .NET base classes and using a more modern algorithm like scrypt might be preferred.
Pseudo Code =>
string passwordFlagLength(string password)
{
int count = 1
for (int i = 0 to 31)
{
if (password.length == count) return password;
if (password.length < count) return password + new string("x", count - password.length);
count = count * 2
}
}
This takes a password and makes it length 1, 2, 4, 8, etc... up to a 31 bit value.(int w/out negative)
if the value is already a proper flag size number, it uses it, otherwise it fills in the rest with "x"
--> Note: I agree with everyone elses comments regarding better was to do security/issues, I just posted this, because the immediate issue posted was due to a string size not being a base 2 bit value. I also just applied the count which I forgot in my original code

How can I use ConvertTo-SecureString

Let's say I need to do this in Powershell:
$SecurePass = Get-Content $CredPath | ConvertTo-SecureString -Key (1..16)
[String]$CleartextPass = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($CredPass));
The content of $CredPath is a file that contains the output of ConvertFrom-SecureString -Key (1..16).
How do I accomplish the ConvertTo-SecureString -key (1..16) portion in C#/.NET?
I know how to create a SecureString, but I'm not sure how the encryption should be handled.
Do I encrypt each character using AES, or decrypt the string and then create a the secure string per character?
I know next to nothing about cryptography, but from what I've gathered I might just want to invoke the Powershell command using C#.
For reference, I found a similar post about AES encryption/decryption here:
Using AES encryption in C#
UPDATE
I have reviewed the link Keith posted, but I face additional unknowns. The DecryptStringFromBytes_Aes takes three arguments:
static string DecryptStringFromBytes_Aes(byte[] cipherText, byte[] Key, byte[] IV)
The first argument is a byte array represents the encrypted text. The question here is, how should the string be represented in the byte array? Should it be represented with or without encoding?
byte[] ciphertext = Encoding.ASCII.GetBytes(encrypted_text);
byte[] ciphertext = Encoding.UTF8.GetBytes(encrypted_text);
byte[] ciphertext = Encoding.Unicode.GetBytes(encrypted_text);
byte[] ciphertext = new byte[encrypted_password.Length * sizeof(char)];
System.Buffer.BlockCopy(encrypted_password.ToCharArray(), 0, text, 0, text.Length);
The second byte array is the key should simply be an array of integers:
byte[] key = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16 };
The third byte array is an "Initialization Vector" - it looks like the Aes.Create() call will generate a byte[] for IV randomly. Reading around, I've found that I might need to use the same IV. As ConvertFrom-SecureString and ConvertTo-SecureString are able to encrypt/decrypt using simply the key, I am left with the assumption that the IV[] can be random -or- has a static definition.
I have not yet found a winning combination, but I will keep trying.
I know this is an old post. I am posting this for completeness and posterity, because I couldn't find a complete answer on MSDN or stackoverflow. It will be here in case I ever need to do this again.
It is a C# implementation of of powershell's ConvertTo-SecureString with AES encryption (turned on by using the -key option). I will leave it for exercise to code a C# implementation of ConvertFrom-SecureString.
# forward direction
[securestring] $someSecureString = read-host -assecurestring
[string] $psProtectedString = ConvertFrom-SecureString -key (1..16) -SecureString $someSecureString
# reverse direction
$back = ConvertTo-SecureString -string $psProtectedString -key (1..16)
My work is combining answers and re-arranging user2748365's answer to be more readable and adding educational comments! I also fixed the issue with taking a substring -- at the time of this post, his code only has two elements in strArray.
using System.IO;
using System.Text;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Cryptography;
using System.Globalization;
// psProtectedString - this is the output from
// powershell> $psProtectedString = ConvertFrom-SecureString -SecureString $aSecureString -key (1..16)
// key - make sure you add size checking
// notes: this will throw an cryptographic invalid padding exception if it cannot decrypt correctly (wrong key)
public static SecureString ConvertToSecureString(string psProtectedString, byte[] key)
{
// '|' is indeed the separater
byte[] asBytes = Convert.FromBase64String( psProtectedString );
string[] strArray = Encoding.Unicode.GetString(asBytes).Split(new[] { '|' });
if (strArray.Length != 3) throw new InvalidDataException("input had incorrect format");
// strArray[0] is a static/magic header or signature (different passwords produce
// the same header) It unused in our case, looks like 16 bytes as hex-string
// you know strArray[1] is a base64 string by the '=' at the end
// the IV is shorter than the body, and you can verify that it is the IV,
// because it is exactly 16bytes=128bits and it decrypts the password correctly
// you know strArray[2] is a hex-string because it is [0-9a-f]
byte[] magicHeader = HexStringToByteArray(encrypted.Substring(0, 32));
byte[] rgbIV = Convert.FromBase64String(strArray[1]);
byte[] cipherBytes = HexStringToByteArray(strArray[2]);
// setup the decrypter
SecureString str = new SecureString();
SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create();
ICryptoTransform transform = algorithm.CreateDecryptor(key, rgbIV);
using (var stream = new CryptoStream(new MemoryStream(cipherBytes), transform, CryptoStreamMode.Read))
{
// using this silly loop format to loop one char at a time
// so we never store the entire password naked in memory
int numRed = 0;
byte[] buffer = new byte[2]; // two bytes per unicode char
while( (numRed = stream.Read(buffer, 0, buffer.Length)) > 0 )
{
str.AppendChar(Encoding.Unicode.GetString(buffer).ToCharArray()[0]);
}
}
//
// non-production code
// recover the SecureString; just to check
// from http://stackoverflow.com/questions/818704/how-to-convert-securestring-to-system-string
//
IntPtr valuePtr = IntPtr.Zero;
string secureStringValue = "";
try
{
// get the string back
valuePtr = Marshal.SecureStringToGlobalAllocUnicode(str);
secureStringValue = Marshal.PtrToStringUni(valuePtr);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
}
return str;
}
// from http://stackoverflow.com/questions/311165/how-do-you-convert-byte-array-to-hexadecimal-string-and-vice-versa
public static byte[] HexStringToByteArray(String hex)
{
int NumberChars = hex.Length;
byte[] bytes = new byte[NumberChars / 2];
for (int i = 0; i < NumberChars; i += 2) bytes[i / 2] = Convert.ToByte(hex.Substring(i, 2), 16);
return bytes;
}
public static SecureString DecryptPassword( string psPasswordFile, byte[] key )
{
if( ! File.Exists(psPasswordFile)) throw new ArgumentException("file does not exist: " + psPasswordFile);
string formattedCipherText = File.ReadAllText( psPasswordFile );
return ConvertToSecureString(formattedCipherText, key);
}
According to the docs on ConvertFrom-SecureString the AES encryption algorithm is used:
If an encryption key is specified by using the Key or SecureKey
parameters, the Advanced Encryption Standard (AES) encryption
algorithm is used. The specified key must have a length of 128, 192,
or 256 bits because those are the key lengths supported by the AES
encryption algorithm. If no key is specified, the Windows Data
Protection API (DPAPI) is used to encrypt the standard string
representation.
Look at the DecryptStringFromBytes_Aes example in the MSDN docs.
BTW an easy option would be to use the PowerShell engine from C# to execute the ConvertTo-SecureString cmdlet to do the work. Otherwise, it looks like the initialization vector is embedded somewhere in the ConvertFrom-SecureString output and may or may not be easy to extract.
How do I accomplish the ConvertTo-SecureString -key (1..16) portion in C#/.NET?
Please see the following code:
private static SecureString ConvertToSecureString(string encrypted, string header, byte[] key)
{
string[] strArray = Encoding.Unicode.GetString(Convert.FromBase64String(encrypted.Substring(header.Length, encrypted.Length - header.Length))).Split(new[] {'|'});
SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create();
int num2 = strArray[2].Length/2;
var bytes = new byte[num2];
for (int i = 0; i < num2; i++)
bytes[i] = byte.Parse(strArray[2].Substring(2*i, 2), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture);
ICryptoTransform transform = algorithm.CreateDecryptor(key, Convert.FromBase64String(strArray[1]));
using (var stream = new CryptoStream(new MemoryStream(bytes), transform, CryptoStreamMode.Read))
{
var buffer = new byte[bytes.Length];
int num = stream.Read(buffer, 0, buffer.Length);
var data = new byte[num];
for (int i = 0; i < num; i++) data[i] = buffer[i];
var str = new SecureString();
for (int j = 0; j < data.Length/2; j++) str.AppendChar((char) ((data[(2*j) + 1]*0x100) + data[2*j]));
return str;
}
}
Example:
encrypted = "76492d1116743f0423413b16050a5345MgB8ADcAbgBiAGoAVQBCAFIANABNADgAYwBSAEoAQQA1AGQAZgAvAHYAYwAvAHcAPQA9AHwAZAAzADQAYwBhADYAOQAxAGIAZgA2ADgAZgA0AGMANwBjADQAYwBiADkAZgA1ADgAZgBiAGQAMwA3AGQAZgAzAA==";
header = "76492d1116743f0423413b16050a5345";
If you want to get decrypted characters, please check data in the method.
I found the easiest and simplest way was to call the ConvertTo-SecureString PowerShell command directly from C#. That way there's no difference in the implementation and the output is exactly what it would be if you called it from PowerShell directly.
string encryptedPassword = RunPowerShellCommand("\""
+ password
+ "\" | ConvertTo-SecureString -AsPlainText -Force | ConvertFrom-SecureString", null);
public static string RunPowerShellCommand(string command,
Dictionary<string, object> parameters)
{
using (PowerShell powerShellInstance = PowerShell.Create())
{
// Set up the running of the script
powerShellInstance.AddScript(command);
// Add the parameters
if (parameters != null)
{
foreach (var parameter in parameters)
{
powerShellInstance.AddParameter(parameter.Key, parameter.Value);
}
}
// Run the command
Collection<PSObject> psOutput = powerShellInstance.Invoke();
StringBuilder stringBuilder = new StringBuilder();
if (powerShellInstance.Streams.Error.Count > 0)
{
foreach (var errorMessage in powerShellInstance.Streams.Error)
{
if (errorMessage != null)
{
throw new InvalidOperationException(errorMessage.ToString());
}
}
}
foreach (var outputLine in psOutput)
{
if (outputLine != null)
{
stringBuilder.Append(outputLine);
}
}
return stringBuilder.ToString();
}
}
Adding on to Cheng's answer - I found I had to change:
byte[] magicHeader = HexStringToByteArray(encrypted.Substring(0, 32));
to
byte[] magicHeader = HexStringToByteArray(psProtectedString.Substring(0, 32));
and
SymmetricAlgorithm algorithm = SymmetricAlgorithm.Create();
to
SymmetricAlgorithm algorithm = Aes.Create();
but it otherwise works wonderfully.

md5 hash confusion

My company uses the following algorithm to hash passwords before store it in the database:
public static string Hash(string value)
{
byte[] valueBytes = new byte[value.Length * 2];
Encoder encoder = Encoding.Unicode.GetEncoder();
encoder.GetBytes(value.ToCharArray(), 0, value.Length, valueBytes, 0, true);
MD5 md5 = new MD5CryptoServiceProvider();
byte[] hashBytes = md5.ComputeHash(valueBytes);
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < hashBytes.Length; i++)
{
stringBuilder.Append(hashBytes[i].ToString("x2"));
}
return stringBuilder.ToString();
}
To me it sounds like a trivial md5 hash, but when I tried to match a password (123456) the algorithm gives me ce0bfd15059b68d67688884d7a3d3e8c, and when I use a standard md5 hash it gives me e10adc3949ba59abbe56e057f20f883e.
A iOS version of the site is being build, and the users needs to login, the password will be hashed before sent. I told the iOS team to use a standard md5 hash, but of course it don't worked out.
I can't unhash the password and hash it again using the standard md5 (of course), and I don't know what exactly tell to the iOS team, in order to get the same hash.
Any help?
You need to use the same encoding on both ends (probably UTF8).
If you replace your code with
byte[] hashBytes = md5.ComputeHash(Encoding.UTF8.GetBytes("123456"));
, you'll get e10adc3949ba59abbe56e057f20f883e.
You need to use UTF8 instead of Unicode. The following code works exactly like the PHP md5() function:
public static string md5(string value)
{
byte[] encoded = ASCIIEncoding.UTF8.GetBytes(value);
MD5CryptoServiceProvider md5Provider = new MD5CryptoServiceProvider();
byte[] hashCode = md5Provider.ComputeHash(encoded);
string ret = "";
foreach (byte a in hashCode)
ret += String.Format("{0:x2}", a);
return ret;
}

Categories

Resources