在Ruby中重新实现ASP.NET会员资格和用户密码哈希处理

10

我有一个大用户数据库(约20万人),我正在将其从ASP.NET应用程序转移到Ruby on Rails应用程序。 我不想要求每个用户重置密码,因此我正在尝试在Ruby中重新实现C#密码哈希函数。

旧的函数是:

public string EncodePassword(string pass, string saltBase64)
 {
     byte[] bytes = Encoding.Unicode.GetBytes(pass);
     byte[] src = Convert.FromBase64String(saltBase64);
     byte[] dst = new byte[src.Length + bytes.Length];
     Buffer.BlockCopy(src, 0, dst, 0, src.Length);
     Buffer.BlockCopy(bytes, 0, dst, src.Length, bytes.Length);
     HashAlgorithm algorithm = HashAlgorithm.Create("SHA1");
     byte[] inArray = algorithm.ComputeHash(dst);
     return Convert.ToBase64String(inArray);
 }

一个示例的哈希密码和盐值是(使用的密码为“password”):

哈希密码:“weEWx4rhyPtd3kec7usysxf7kpk =” 盐值:“1ptFxHq7ALe7yXIQDdzQ9Q==” 密码:“password”

现在使用以下Ruby代码:

require "base64"
require "digest/sha1"


password = "password"
salt = "1ptFxHq7ALe7yXIQDdzQ9Q=="

concat = salt+password

sha1 = Digest::SHA1.digest(concat)

encoded = Base64.encode64(sha1)

puts encoded

我没有得到正确的密码哈希值(我得到了“+BsdIOBN/Vh2U7qWG4e+O13h3iQ=”而不是“weEWx4rhyPtd3kec7usysxf7kpk=”)。有人能看出问题在哪里吗?

非常感谢

Arfon

4个回答

9

简单地更新一下,我的一个同事已经解决了这个问题:

require "base64"
require "digest"
require "jcode"


def encode_password(password, salt)
 bytes = ""
 password.each_char { |c| bytes += c + "\x00" }
 salty = Base64.decode64(salt)
 concat = salty+bytes
 sha1 = Digest::SHA1.digest(concat)
 encoded = Base64.encode64(sha1).strip()
 puts encoded
end

3
我被委托将一个现有的.NET应用程序迁移到Ruby on Rails。我正在使用下面的代码来模拟.NET密码哈希。我非常新于Ruby,不了解.NET。代码可能不够干净,但这是一个开始。
为了测试,请将此保存为Ruby脚本并运行:
ruby script plain_text_password salt_in_base64
例如:
ruby dotNetHash.rb password123 LU7hUk4MXAvlq6DksvP9SQ==
require "base64"
require "digest"

# Encode password as double-width characters
password_as_text = ARGV.first
double_width_password = []
double_width_password = password_as_text.encode("UTF-16LE").bytes.to_a

# Unencode the salt
salt = Base64.decode64(ARGV[1])

# Concatenate salt+pass
salt_pass_array = []
salt_pass_array = salt.bytes.to_a + double_width_password

# Repack array as string and hash it. Then encode.
salt_pass_str = salt_pass_array.pack('C*')
sha1_saltpass = Digest::SHA1.digest(salt_pass_str)
enc_sha1_saltpass = Base64.encode64(sha1_saltpass).strip()
puts "Encoded SHA1 saltpass is " + enc_sha1_saltpass

哇,伙计,我一直在尝试这个,但是你的代码做到了。我想你正在尝试复制非常相似的代码。我尝试了很多不同的方法,离你的代码非常接近,但它只是稍微不同。我使用了“S*”进行打包和解包。非常感谢。 - Amala

2
你已经很接近了。不幸的是,目前 Ruby 没有内置的 Unicode 支持,而你的哈希函数依赖于它。有一些解决方法,请在网站上查找如何在 Ruby 中处理 Unicode。 另外,我认为你忘记对盐进行 base64 解码了,它看起来像 ASP.net 函数会这样做。

1
你需要对盐进行解码,将其转换回字节表示,然后将其与密码连接起来以获取哈希密码值。你正在直接使用编码盐字符串(它是不同的盐),因此它会散列成不同的结果。
require "base64"
require "digest/sha1"
password = "password"
salt = Base64.decode64("1ptFxHq7ALe7yXIQDdzQ9Q==")
concat = salt+password
sha1 = Digest::SHA1.digest(concat)
encoded = Base64.encode64(sha1)
puts encoded

你正在跳过一个重要的步骤:将密码转换为字节。 - Adam Lassek
好的,看起来我得降到字节级别才能做到这个? - arfon
@Adam -- 不确定。在.NET中,它将它们放入一个字节数组中,以便交给哈希算法处理。Ruby可能会使用简单的字符串来实现相同的功能。如果它们被存储为以null结尾的,则与.NET示例中的缓冲区基本相同。我建议你试一下,看看是否可行。 - tvanfosson
这里可能存在一个潜在的问题,即 Ruby Digest 库无法对字节数组进行哈希,只能对字符串进行哈希。 - arfon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接