![](/img/trans.png)
[英]Could not load type 'System.Security.Cryptography.SHA256Managed' from assembly 'mscorlib, Version=4.0.0.0
[英]How to reproduce System.Security.Cryptography.SHA1Managed result in Python
交易如下:我正在將一個 .NET 網站遷移到 Python。 我有一個使用 System.Security.Cryptography.SHA1Managed 實用程序散列密碼的數據庫。
我正在使用以下代碼在 .NET 中創建哈希:
string hashedPassword = Cryptographer.CreateHash("MYHasher", userInfo.Password);
MYHasher 塊如下所示:
<add algorithmType="System.Security.Cryptography.SHA1Managed, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=blahblahblah"
saltEnabled="true" type="Microsoft.Practices.EnterpriseLibrary.Security.Cryptography.HashAlgorithmProvider, Microsoft.Practices.EnterpriseLibrary.Security.Cryptography, Version=3.0.0.0, Culture=neutral, PublicKeyToken=daahblahdahdah"
name="MYHasher" />
因此,對於給定的密碼,我返回並在數據庫中存儲一個 48 字節的加鹽 sha1。 我假設最后 8 個字節是鹽。 我試圖通過執行 sha1(salt + password) 和 sha1(password + salt) 在 python 中重現散列過程,但我沒有運氣。
我對你的問題:
我需要不只是引用其他 .NET 庫的特定細節,我正在尋找發生在黑匣子中的實際操作邏輯。
謝謝!
抱歉回復晚了,但我在嘗試復制 Enterprise Library 的 Cryptography Block 中使用的 SHA1 散列邏輯時遇到了類似的情況,但使用的是 Java。
要回答您的每個問題:
如何使用公鑰?
上面配置塊中的 PublicKeyToken 用於標識一個簽名的、強命名的 .net 程序集。 這是公鑰的 64 位散列,對應於用於簽署程序集的私鑰。 注意:此密鑰與您對散列數據的實現完全無關。
如何使用鹽重新哈希密碼。
使用鹽創建散列密碼的事件序列如下:
調用Cryptographer.CreateHash("MYHasher",value);
其中"MYHasher"
是配置塊中指定的已配置System.Security.Cryptography.SHA1Managed
實例提供程序的名稱, value
是要散列的字符串。
上述方法調用CreateHash(IHashProvider provider, string plaintext)
,其中提供了已解析的IHashProvider
。 在此方法中,運行以下代碼:
byte[] bytes = Encoding.Unicode.GetBytes(plaintext); byte[] hash = provider.CreateHash(bytes); CryptographyUtility.GetRandomBytes(bytes); return Convert.ToBase64String(hash);
開始時傳遞的value
參數(現在是plaintext
參數)使用 Unicode 編碼轉換為字節數組。
接下來,使用上面創建的字節數組調用 SHA1 哈希提供程序的CreateHash(bytes)
方法。 在此方法中,發生以下步驟:
this.CreateHashWithSalt(plaintext, (byte[]) null);
被調用,其中plaintext
是一個字節數組,包含在堆棧頂部作為字符串傳入的原始value
。 第二個參數是鹽字節數組(為空)。 在此方法中,調用了以下代碼:
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)
是關於提供的文本如何被加鹽的第一個線索。 在此方法中,運行以下代碼: if (!this.saltEnabled) return; if (salt == null) salt = CryptographyUtility.GetRandomBytes(16); plaintext = CryptographyUtility.CombineBytes(salt, plaintext);
this.saltEnabled
變量由配置塊中的saltEnabled="true"
初始化。 如果為 true,並且您沒有提供 salt,則會為您生成一個包含 16 個隨機字節的字節數組(通過調用外部 C API)。plaintext
變量然后在它前面加上鹽。 例如:[鹽][明文]這一點很重要!
然后通過調用this.HashCryptographer.ComputeHash(plaintext);
對鹽和plaintext
的組合進行 SHA1 哈希處理this.HashCryptographer.ComputeHash(plaintext);
. 這將產生一個 20 字節長的數組。
然后,通過調用this.AddSaltToHash(salt, ref hash);
再次將鹽添加到先前創建的 20 字節數組中this.AddSaltToHash(salt, ref hash);
, 給你一個 36 字節長的數組。
返回堆棧最終將引導您調用return Convert.ToBase64String(hash);
在CreateHash()
方法中。 這將返回提供的 SHA1 加鹽散列值 + 鹽的 Base64 字符串表示。
公式:Base64(salt + SHA1(salt + value))
鹽是如何產生的? (例如,當我說 saltEnabled="true" 時,會發生什么額外的魔法?)
這在問題 2 中得到了回答,特別是對CryptographyUtility.GetRandomBytes(16);
的調用CryptographyUtility.GetRandomBytes(16);
最終調用一個 C 庫:
[DllImport("QCall", CharSet = CharSet.Unicode)] private static extern void GetBytes(SafeProvHandle hProv, byte[] randomBytes, int count);
希望這在某種程度上有所幫助!
根據這個以前的線程,這應該類似於 sha1(password+salt)+salt。 SHA-1 輸出是 20 個字節,因此對於 48 個字節,這應該是 28 字節的鹽,而不是 8 字節的鹽,除非使用了某種編碼。
當您使用string CreateHash(string, string)
重載時,會發生以下情況:
謝謝加雷斯·斯蒂芬森! 你的回答有我需要的所有答案。 我完全迷失了這個。 我需要升級一個使用這個企業庫的遺留模塊,但是編譯時有很多問題,我無法調試代碼。 保持代碼打開了無數其他問題,包括依賴項和公鑰令牌不匹配/版本。 所以我根據 Gareth 的回答重新編寫了所需的函數。 我最終找到了配置文件中使用的加密。 這可以在 app.config(在我的情況下)、web.config 或其他配置中的某處:
<securityCryptographyConfiguration>
<hashProviders>
<add algorithmType="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>
我寫的代碼是:
//Because of the random salt added, each time you hash a password it will create a new result.
public static string GetHashedValue(string password)
{
//this will create a new hash?
//Hashed Password Formula: Base64(salt + Sha1(salt + value))
var crypto = new SHA1CryptoServiceProvider();
byte[] saltBytes = new byte[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);
}
並檢查密碼的有效性:
public static bool IsPasswordValid(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 = new byte[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;
}
就在我以為我必須重置所有客戶的密碼之前……我希望有一天這也能保護其他人!
謝謝@Leo Muler,你的 csharp 代碼幫助我把它翻譯成 nodejs。
這是代碼:
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('');
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.