Python中的hashlib与crypt.crypt()的比较。为什么结果不同?

2024-04-20 15:27:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我在学Python。我不明白为什么hashlib.sha512(salt + password).hexdigest()没有给出预期的结果。

我正在寻找一个与Ulrich dreper的sha512crypt.c algorithm等价的纯Python实现。(我花了一段时间才弄明白我在找什么。)

根据我的Ubuntu 12.04系统上crypt的手册页,crypt正在使用SHA-512(因为字符串以$6$开头)。

下面的代码验证当我调用系统crypt的Python包装器(即crypt.crypt())时,行为是否如预期的那样。我想使用hashlib.sha512或其他一些Python库生成与crypt.crypt()相同的结果。怎样?

这段代码显示了我遇到的问题:

import hashlib, crypt

ctype = "6" #for sha512 (see man crypt)
salt = "qwerty"
insalt = '${}${}$'.format(ctype, salt)
password = "AMOROSO8282"

value1 = hashlib.sha512(salt + password).hexdigest() #what's wrong with this one?
value2 = crypt.crypt(password, insalt) #this one is correct on Ubuntu 12.04
if not value1 == value2:
    print("{}\n{}\n\n".format(value1, value2))

根据地穴手册,SHA-512有86个字符。上面代码中的crypt()调用符合这一点。然而,hashlib.sha512的输出比86个字符长,所以这两个实现之间的差距很大。。。

下面是那些不想运行代码的人的输出:

051f606027bd42c1aae0d71d049fdaedbcfd28bad056597b3f908d22f91cbe7b29fd0cdda4b26956397b044ed75d50c11d0c3331d3cb157eecd9481c4480e455
$6$qwerty$wZZxE91RvJb4ETR0svmCb69rVCevicDV1Fw.Y9Qyg9idcZUioEoYmOzAv23wyEiNoyMLuBLGXPSQbd5ETanmq/

另一个基于初始反馈的尝试。尚未成功:

import hashlib, crypt, base64

ctype = "6" #for sha512 (see man crypt)
salt = "qwerty"
insalt = '${}${}$'.format(ctype, salt)
password = "AMOROSO8282"

value1 = base64.b64encode(hashlib.sha512(salt + password).digest())
value2 = crypt.crypt(password, insalt) #this one is correct
if not value1 == value2:
    print("{}\n{}\n\n".format(value1, value2))

Tags: 代码formatpasswordthisctypeonehashlibsalt
3条回答

这是解决办法。在另一个问题上还有更多的细节:Python implementation of sha512_crypt.c,它显示passlib的后端包含一个sha512_crypt的纯Python实现(如果crypt.crypt()在操作系统上不可用,则调用Python实现)。

$sudo pip安装passlib

import passlib.hash, crypt

ctype = "6" #for sha512 (see man crypt)
salt = "qwerty"
insalt = '${}${}$'.format(ctype, salt)
password = "AMOROSO8282"

value1 = sha512_crypt.encrypt(password, salt=salt, rounds=5000)
value2 = crypt.crypt(password, insalt)
if not value1 == value2:
    print("algorithms do not match")
print("{}\n{}\n\n".format(value1, value2))

输出如下:

$6$qwerty$wZZxE91RvJb4ETR0svmCb69rVCevicDV1Fw.Y9Qyg9idcZUioEoYmOzAv23wyEiNoyMLuBLGXPSQbd5ETanmq/
$6$qwerty$wZZxE91RvJb4ETR0svmCb69rVCevicDV1Fw.Y9Qyg9idcZUioEoYmOzAv23wyEiNoyMLuBLGXPSQbd5ETanmq/

一个关键点是Passlib有一个纯Python的sha512_crypt实现,当系统没有当前Linux系统所具有的crypt实现(例如http://www.akkadia.org/drepper/SHA-crypt.txt)时,它将使用该实现。

请参见此处的PassLib文档:

passlib-python密码散列库-Google项目宿主
https://code.google.com/p/passlib/

Passlib 1.6.2文档-Passlib v1.6.2文档
http://pythonhosted.org/passlib/

passlib用户-Google组
https://groups.google.com/forum/#!forum/passlib-users

新应用程序快速启动指南-Passlib v1.6.2文档
http://pythonhosted.org/passlib/new_app_quickstart.html#sha512-crypt

passlib.hash.sha512_crypt-SHA-512 crypt-passlib v1.6.2文档
http://pythonhosted.org/passlib/lib/passlib.hash.sha512_crypt.html#passlib.hash.sha512_crypt

您的密码长度不同,这是因为crypt() 输出是base64编码的,使用hexdigest进行value1

你应该试着做一些像

value1 = crypt_base64(hashlib.sha512(salt + password))

crypt_base64类似的bash implementation,是 doHash()功能。

手册是不精确的(甚至是误导性的)。带有“MD5”、“SHA-256”或“SHA-512”名字的crypt使用的算法实际上是在这些原语上构建的算法。它们是password-based key derivation functions,使用哈希来执行key strengthening

一个good password hashing algorithm有两个属性:它必须将密码与一个唯一的salt组合起来(以挫败一次破解许多密码的尝试),并且它必须很慢(因为这对攻击者的伤害大于对防御者的伤害,因为攻击者需要尝试大量的组合)。像MD5和SHA家族这样的日常散列算法被设计成快速的,尽可能快的,同时仍然具有所需的安全属性。构建密码哈希算法的一种方法是采用加密哈希算法并多次迭代。虽然这个isn't ideal(因为有更好的技术使得构建用于密码破解的专用硬件更加困难),但它已经足够了。

Wikipedia article for ^{}提供了一个简短的解释,并有指向主要源的指针。Linux和FreeBSD的手册页很差,但是Solaris's有足够的信息,不会产生误导(请访问^{}的链接,然后是^{}和其他链接)。您还可以读取Is user password in ubuntu 13.04 in plain text?Is there repetition in the Solaris 11 hash routine? Can I add some?

在Python中计算crypt输出的正确方法是调用crypt.crypt

相关问题 更多 >