Here\'s the deal: I\'m moving a .NET website to Python. I have a database with passwords hashed using the System.Security.Cryptography.SHA1Managed utility.
I\'m cre
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 configured System.Security.Cryptography.SHA1Managed
instance provider specified in your configuration block, and value
is the string to be hashed.
The above method makes a call to CreateHash(IHashProvider provider, string plaintext)
, where a resolved IHashProvider
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 the plaintext
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, where plaintext
is a byte array containing the original value
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);
this.saltEnabled
variable is initialised by the saltEnabled="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).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 calling this.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 the CreateHash()
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!