How To Reproduce System.security.cryptography.sha1managed Result In Python
Solution 1:
Sorry for the late reply, but I've just come across a similar situation while trying to replicate the SHA1 hashing logic used in the Enterprise Library's Cryptography Block, but with using Java.
To answer each of your questions:
How are the public keys being used?
The PublicKeyToken in the configuration block above is used to identify a signed, strong-named .net assembly. This is a 64-bit hash of the public key that corresponds to the private key used to sign the assembly. NOTE: This key has absolutely no bearing on your implementation to hash data.
How is the password rehashed using the salt.
The sequence of events to create the hashed password with the salt is as follows:
Call
Cryptographer.CreateHash("MYHasher",value);
where"MYHasher"
is the name of the configuredSystem.Security.Cryptography.SHA1Managed
instance provider specified in your configuration block, andvalue
is the string to be hashed.The above method makes a call to
CreateHash(IHashProvider provider, string plaintext)
, where a resolvedIHashProvider
is supplied. Inside this method, the following code is run:
byte[] bytes = Encoding.Unicode.GetBytes(plaintext); byte[] hash = provider.CreateHash(bytes); CryptographyUtility.GetRandomBytes(bytes); return Convert.ToBase64String(hash);
The
value
argument that was passed right at the beginning (which is now theplaintext
argument) is converted into a byte array, using Unicode encoding.Next, the SHA1 hash provider's
CreateHash(bytes)
method is called with the byte array created above. Inside this method, the following steps occur:this.CreateHashWithSalt(plaintext, (byte[]) null);
is called, whereplaintext
is a byte array containing the originalvalue
passed in at the top of the stack as a string. The second argument is the salt byte array (which is null). Inside this method, the following code is called:
this.AddSaltToPlainText(ref salt, ref plaintext); byte[] hash = this.HashCryptographer.ComputeHash(plaintext); this.AddSaltToHash(salt, ref hash); return hash;
this.AddSaltToPlainText(ref salt, ref plaintext)
is the first clue as to how the supplied text is salted. Inside this method, the following code runs:
if (!this.saltEnabled) return; if (salt == null) salt = CryptographyUtility.GetRandomBytes(16); plaintext = CryptographyUtility.CombineBytes(salt, plaintext);
- The
this.saltEnabled
variable is initialised by thesaltEnabled="true"
in your configuration block. If true, and if you haven't supplied a salt, a byte array of 16 random bytes will be generated for you (via calling an external C API). - The
plaintext
variable then has the salt prepended to it. e.g.: [salt][plaintext]
This is very important to note!
The combination of the salt and
plaintext
are then SHA1-hashed by callingthis.HashCryptographer.ComputeHash(plaintext);
. This will produce a 20 byte long array.Then, the salt is prepended again to the 20 byte array created previously, via the call
this.AddSaltToHash(salt, ref hash);
, to give you a 36 byte long array.Going back up the stack will eventually lead you to the call
return Convert.ToBase64String(hash);
inside theCreateHash()
method. This will return the Base64 string representation of the SHA1 salted hashed value + salt that was supplied.
Formula: Base64(salt + SHA1(salt + value))
How is the salt created? (e.g., When I say saltEnabled="true", what extra magic happens?)
This was answered in question 2, specifically the call to
CryptographyUtility.GetRandomBytes(16);
which eventually calls a C library:
[DllImport("QCall", CharSet = CharSet.Unicode)]
private static extern void GetBytes(SafeProvHandle hProv, byte[] randomBytes, int count);
Hope this helps in some way!
Solution 2:
According to this previous thread, this should be something like sha1(password+salt)+salt. SHA-1 output is twenty bytes, so for 48 bytes this should be a 28-byte salt, not an 8-byte salt, unless some sort of encoding was used.
Solution 3:
When you use the string CreateHash(string, string)
overload, the following occurs:
- The string is converted to bytes using UTF16 (using Encoding.Unicode.GetBytes()).
- A random 16-byte salt is generated.
- The salt is appended to the converted string and hashed.
- The salt is appended to the hash.
- The hash+salt is converted back to a string using base64 (using Convert.ToBase64String()).
Solution 4:
Thanks Gareth Stephenson! your answer had all the answers I needed. I was getting completely lost with this. I needed to upgrade a legacy module that was using this enterprise library, but there were so many problems with compiling I couldn't debug the code. Keeping the code opened a zillion other problems with dependencies and public key token mismatches / versions. So I re-wrote the functions needed based on Gareth's answer. I eventually found the encryption used in the config file. This can be in the app.config (in my case), web.config or other config somewhere:
<securityCryptographyConfiguration><hashProviders><addalgorithmType="System.Security.Cryptography.SHA1Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"saltEnabled="true"type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=2.0.0.0, Culture=neutral, PublicKeyToken=06300324c959bce8"name="ABC" /></hashProviders>
The code I wrote is:
//Because of the random salt added, each time you hash a password it will create a new result.publicstaticstringGetHashedValue(string password)
{
//this will create a new hash?//Hashed Password Formula: Base64(salt + Sha1(salt + value))var crypto = new SHA1CryptoServiceProvider();
byte[] saltBytes = newbyte[16];
RandomNumberGenerator.Create().GetBytes(saltBytes);
byte[] checkPasswordBytes = Encoding.Unicode.GetBytes(password);
byte[] tempResult = crypto.ComputeHash(saltBytes.Concat(checkPasswordBytes).ToArray()); //ComputeHash(salt + value)byte[] resultBytes = saltBytes.Concat(tempResult).ToArray(); //salt + ComputeHash(salt + value)return Convert.ToBase64String(resultBytes);
}
and to check the validity of a password:
publicstaticboolIsPasswordValid(string passwordToCheck, string savedPassword)
{
bool retVal = false;
var crypto = new SHA1CryptoServiceProvider();
//get the salt, which is part of the saved password. These are the first 16 bytes.byte[] storedPasswordBytes = Convert.FromBase64String(savedPassword);
byte[] saltBytes = newbyte[16];
Array.Copy(storedPasswordBytes, saltBytes, 16);
//hash the password that you want to check with the same salt and the same algoritm:byte[] checkPasswordBytes = Encoding.Unicode.GetBytes(passwordToCheck);
byte[] tempResult = crypto.ComputeHash(saltBytes.Concat(checkPasswordBytes).ToArray()); //ComputeHash(salt + value)byte[] resultBytes = saltBytes.Concat(tempResult).ToArray(); //salt + ComputeHash(salt + value)string resultString = Convert.ToBase64String(resultBytes);
if (savedPassword == resultString)
{
retVal = true;
}
return retVal;
}
And that just before I thought I would have to reset all my customers' passwords... I hope this will safe someone else as well one day!
Solution 5:
Thanks @Leo Muler , your csharp code helped me a lot to translate it into nodejs.
Here is the code :
const saltLength = 16;
const cryptedPwd = 'm2gFufL1WYJEcjdgnu4Eo0qXHM8+whC75AMnYxCS+uRbiS4OBy5+4TKNQbiSJyTG';
const pwd = 'myPassword';
let binaryPwd = Buffer.from(cryptedPwd, 'base64');
let salt = binaryPwd.slice(0, saltLength);
let saltBuffer = [...salt];
let bytePwd = Buffer.from(pwd, 'utf16le');
let pwdBuffer = [...bytePwd];
let saltAndPwd = saltBuffer.concat(pwdBuffer);
let saltAndPwdBinary = Buffer.from(saltAndPwd).toString('utf16le');
let cryptedBuffer = Array.from(crypto.createHash('sha256').update(saltAndPwdBinary, 'utf16le').digest());
let concatCryptedBuffer = saltBuffer.concat(cryptedBuffer);
let cryptedString = Buffer.from(concatCryptedBuffer).toString('base64');
console.log('cryptedString : ' + cryptedString);
console.log('same : ' + (cryptedString == cryptedPwd));
console.log('');
Post a Comment for "How To Reproduce System.security.cryptography.sha1managed Result In Python"