如何将PHP crypt实现转换为Ruby?

如何将PHP crypt实现转换为Ruby?,php,ruby-on-rails,ruby,authentication,ruby-on-rails-6,Php,Ruby On Rails,Ruby,Authentication,Ruby On Rails 6,我正在mac上开发并部署到Linux。我正在现有PHP应用程序的基础上编写Rails API,因此我无法更改密码或密码,但我需要身份验证才能工作。我需要能够对现有密码进行身份验证,然后最终更改密码,使原始PHP应用程序仍然能够读取密码。我有权使用确切的密码功能。我需要一个Ruby解决方案(可以使用gem) PHP中的代码(更改salt以保护无辜者)如下所示: $passwordSHA = crypt($_POST['pass'],'$6$rounds=21000$abcdefghijklmnop

我正在mac上开发并部署到Linux。我正在现有PHP应用程序的基础上编写Rails API,因此我无法更改密码或密码,但我需要身份验证才能工作。我需要能够对现有密码进行身份验证,然后最终更改密码,使原始PHP应用程序仍然能够读取密码。我有权使用确切的密码功能。我需要一个Ruby解决方案(可以使用gem)

PHP中的代码(更改salt以保护无辜者)如下所示:

$passwordSHA = crypt($_POST['pass'],'$6$rounds=21000$abcdefghijklmnopqrstuv$');
我正在尝试编写一个函数来验证用户提供的密码是否与数据库中的加密密码匹配。我试着用正确的盐来做建议的事情,但没有成功(这可能只是因为没有正确地包括回合数)

存储在数据库中的密码看起来(sorta,为了安全而更改)是这样的。请注意,开头总是
$6$rounds=21000$zxyababcdefghijklm$

$6$rounds=21000$zxyabcdefghijklm$F/L4T80nbaaaaLMb7nPJ2OV5H/aaa.00v900000/Z5jlTLSa.XXXXXX./444444/p8b61UBz9z2Bj4qsABC4.
即使使用OpenSSL和BCrypt,我也尝试了一些不同的方法,但我不知道如何从
$6
开始

更新:尝试第一个答案,我没有得到任何接近正确长度的答案。也许这些信息有助于找出这不起作用的原因:

2.5.3 :001 > ruby_crypt = "foo".crypt('$6$rounds=21000$salt$')
 => "$6A86JNndVTdM"
更新2:请注意盐的长度。它比UnixCrypt允许的最大长度16长

更新3/ANSWER:这里的问题是salt应该被截断超过16个字符。然后,PHP在后面插入舍入计数。最后的答案是:

hashed_password = UnixCrypt::SHA512.build(password, salt, 5000).gsub('$6', '$6$rounds=21000')
为了方便起见,这似乎是可行的:

require 'unix_crypt'

PHP_CMD = %q|php -r 'echo crypt("foo", "\$6\$rounds=21000\$salt\$");'|
php_crypt = `#{PHP_CMD}`

puts "PHP:"
puts php_crypt

# This code is using native crypt and is platform dependent and not portable
# ruby_crypt = 'foo'.crypt('$6$rounds=21000$salt$')

# Because $6 means SHA512, we can use UnixCrypt instead for portability (thanks @matt)
ruby_crypt = UnixCrypt::SHA512.build('foo', 'salt', 21000)

puts "\nRUBY:"
puts ruby_crypt

# If doing password validation only, it is best to use the 'valid?' method,
# as it will handle modifications in the algorithm and salt automatically.
puts "\nValidate:"
p php_crypt == ruby_crypt
p UnixCrypt.valid?('foo', php_crypt)
p UnixCrypt.valid?('foo', ruby_crypt)
输出:

PHP:
$6$rounds=21000$salt$kvEIl.U0dy6F6y9dkTeTwUrpCOpz5eYK.jyTC2LeKo/gq9HYpDtD6.fMlHyLW.5h3fF4v.R19lX8W18KDvper1

RUBY:
$6$rounds=21000$salt$kvEIl.U0dy6F6y9dkTeTwUrpCOpz5eYK.jyTC2LeKo/gq9HYpDtD6.fMlHyLW.5h3fF4v.R19lX8W18KDvper1

Validate:
true
true
true

$6$
是SHA512密码的模块化密码格式标识符代码。它绝对不是BCrypt,它使用的是
2
(即
$2$
$2a$
$2x$
$2y$
,或者
$2b$
,具体取决于版本和参数。)我想尽一切办法尝试过这一点,从Ruby那里我似乎从来没有得到比这更长的东西:
$6OAR9e31dlDg
。这可能和盐的长度有关吗?您的代码输出如下:
2.5.3:001>ruby_crypt=“foo”.crypt('6$rounds=21000$salt$)=>“$6A86JNndVTdM”
您实际上并没有使用unix crypt gem在这里生成密码,而是ruby内置的(这取决于平台,可能是@EricLubow无法获得正确结果的原因)。您应该将
ruby\u crypt=“foo”.crypt('6$rounds=21000$salt$')
更改为
ruby\u crypt=UnixCrypt::SHA512.build('foo','salt',21000)
。(这意味着不能仅仅通过更改salt字符串来更改算法)。@EricLubow如果您只需要在Ruby中进行验证,而不需要生成,那么运行
UnixCrypt.valid?
是最适合您的可移植解决方案。实际上,我最终需要在某个时候进行生成。当我使用UnixCrypt时,会出现错误:
UnixCrypt::SaltToolLongError(不允许使用长度超过16个字符的盐)
当我使用PHP使用的相同盐时。@EricLubow 16字节是SHA512Crypt的最大字节数。Ruby会抛出一个异常,如果您试图使用更长的异常,PHP只会将其截断为16字节。在散列之前,将Ruby中的salt截断为16字节,您应该会得到相同的结果。