我有一个简单的设置,应用服务器支持到 CouchDB,我在其中使用 CouchDB 的内置用户身份验证对应用服务器的用户进行身份验证。应用服务器可以访问 _users 数据库并检索用户的文档。
我正在尝试复制密码哈希函数,这样我就可以在应用服务器中验证用户的密码,而无需针对 CouchDB 进行身份验证。
CouchDB 安全规范指出:
“password_sha”属性是 SHA-1 哈希值的十六进制表示,它通过与用户密码与盐连接的字符串(理想情况下是随机字符串)相匹配的字符串计算得出。salt 属性是用于生成用户密码哈希的盐的十六进制表示。
从 CouchDB 1.2.0 开始,当用户文档中存在密码字段时,会自动创建 password_sha 和 salt 字段。当写入用户文档时,CouchDB 检查密码字段是否存在,如果存在,它将生成一个盐,对密码字段的值进行散列,并对密码散列和盐的连接进行散列。然后它将生成的密码写入 password_sha 字段,并将盐写入 salt 字段。密码字段被删除。
相关源代码:
% Lines 72-74 of couch_httpd_auth.erl
UserSalt = couch_util:get_value(<<"salt">>, UserProps, <<>>),
PasswordHash = hash_password(?l2b(Pass), UserSalt),
ExpectedHash = couch_util:get_value(<<"password_sha">>, UserProps, nil),
% Lines 237-238 of couch_httpd_auth.erl
hash_password(Password, Salt) ->
?l2b(couch_util:to_hex(crypto:sha(<<Password/binary, Salt/binary>>))).
这是我尝试使用密码为“password”的测试用户复制它:
import java.security.MessageDigest
import org.apache.commons.codec.binary.Hex
import org.specs2.mutable.Specification
class PasswordSpec extends Specification {
"Password" should {
"match" in {
val password = "password"
val hexEncodedPasswordHash = "0fed560a9928b50761ebec5aa97c815999e6def0"
val hexEncodedSalt = "2ba345d5f2880fae25de9ec7a78d38ae"
val charset = "UTF-8"
val codec = new Hex(charset)
val md = MessageDigest.getInstance("SHA-1")
md.reset()
md.update(password.getBytes(charset))
md.update(codec.decode(hexEncodedSalt.getBytes(charset)))
val hashBytes = md.digest()
val hexEncodedHash = new String(codec.encode(hashBytes), charset)
hexEncodedHash mustEqual(hexEncodedPasswordHash)
}
}
}
这个测试失败了,我已经不知道为什么了。