[英]How can I with Ruby check if SHA1 username, password and verifier/salt is correct?
大家好,這是我在這里的第一篇文章。
我有一個用戶名,密碼和這個驗證器,鹽。 如何檢查 Ruby 是否正確?
我遵循的文檔/說明是這樣的: https://www.azerothcore.org/wiki/account
我的代碼 atm 如下所示:
class Account < ApplicationRecord
self.table_name = "account"
def self.check_username_password(username, password)
account = Account.find_by(username: username)
h1 = Digest::SHA1.hexdigest(username.uppercase + password.uppercase)
h2 = Digest::SHA1.hexdigest( account.salt + ..... )
h2 == account.verifier
end
end
您鏈接的文檔描述了計算驗證者的算法:
驗證者
verifier 派生自 salt,以及用戶的用戶名(全部大寫)和他們的密碼(全部大寫)。
要獲得驗證者,您需要計算:
計算 h1 = SHA1("USERNAME:PASSWORD"),將用戶的用戶名和密碼替換為大寫。
計算 h2 = SHA1(salt || h1),其中 || 是連接(PHP 中的 . 運算符)。
注意:salt 和 h1 都是二進制,而不是十六進制字符串!
將 h2 視為 integer 以 little-endian 順序(第一個字節是最低有效字節)。
計算 (g ^ h2) % N。
注意:g 和 N 是參數,在 WoW 實現中是固定的。
g = 7
N = 0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7
以 little-endian 順序將結果轉換回字節數組。
根據示例實現的鏈接,這似乎是來自 SRP6 的驗證程序,您可能可以將此 gem 用於: https://github.com/grempe/sirp 。 但是,它與文檔並不完全一致,我認為這可能很有趣,所以無論如何我都會嘗試完成它。
首先,您已經開始查找h1
和h2
,但是正如注釋所說, Both salt and h1 are binary, not hexadecimal strings!
. 因此,您需要將hexdigest
替換為digest
。 此外, upcase
中的大寫方法是大寫的,您需要在兩者之間加一個冒號:
h1 = Digest::SHA1.digest("#{username.upcase}:#{password.upcase}")
h2 = Digest::SHA1.digest(account.salt + h1)
接下來,它將 h2 轉換為 integer,就好像它存儲在 little-endian 中一樣。 請記住,整數存儲為字節序列,每個字節為 8 位; 所以 32 位 integer 是 4 個字節。 字節序描述了第一個字節是映射到數字的前 8 位還是后 8 位。 在這里,評論清楚地表明這將是最后一次。 現在,SHA1 生成一個 20 字節的 hash,所以我們將使用unpack
方法加上H
指令(匹配每個十六進制字節)來得到它。 這部分可能是錯誤的,所以如果您的驗證器仍然不匹配,請嘗試注釋掉的替代版本。
h2_int = h2.reverse.unpack("H*").first.to_i(16)
# alternate version, treating the first byte as most significant:
#
# h2_int = h2.unpack("H*").first.to_i(16)
最后,我們對給定的常量進行一些數學運算並將其轉換回字符串。 ^ %
結構必須是模冪運算,您可以在 Ruby 2.5+ 中僅使用Integer#pow
或在 Ruby 2.4 中使用 openssl 的mod_exp
:
g = 7
N = 0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7
verifier_int = g.pow(h2_int, N)
# ruby 2.4 or below:
#
# require 'openssl'
# verifier_int = g.to_bn.mod_exp(h2_int, N).to_i
verifier = [verifier_int.to_s(16)].pack('H*').reverse
# alternate version, if the above alternate version is also needed:
#
# verifier = [verifier_int.to_s(16)].pack('H*')
把它們放在一起:
h1 = Digest::SHA1.digest("#{username.upcase}:#{password.upcase}")
h2 = Digest::SHA1.digest(account.salt + h1)
h2_int = h2.reverse.unpack("H*").first.to_i(16)
# alternate version, treating the first byte as most significant:
#
# h2_int = h2.unpack("H*").first.to_i(16)
g = 7
N = 0x894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7
verifier_int = g.pow(h2_int, N)
# ruby 2.4 or below:
#
# require 'openssl'
# verifier_int = g.to_bn.mod_exp(h2_int, N).to_i
verifier = [verifier_int.to_s(16)].pack('H*').reverse
# alternate version, if the above alternate version is also needed:
#
# verifier = [verifier_int.to_s(16)].pack('H*')
這可能是錯誤的,但為了進行更多調試,我需要一個示例用戶名和密碼驗證器(但不是來自真實帳戶。)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.