hashlib vs Python中的crypt.crypt()。为何结果不同?

时间:2014-01-11 07:21:33

标签: python passwords encryption cryptography

我正在学习Python。我无法弄清楚为什么hashlib.sha512(salt + password).hexdigest()没有给出预期的结果。

我正在寻找相当于Ulrich Drepper的sha512crypt.c algorithm的纯Python实现。 (我花了一些时间来弄清楚我在寻找什么。)

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

下面的代码验证了当我调用Python的系统crypt包装器(即crypt.crypt())时,行为是否符合预期。我想使用hashlib.sha512或其他一些Python lib来产生与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))

根据crypt手册页,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))

3 个答案:

答案 0 :(得分:11)

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

$ sudo pip install 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有一个sha512_crypt的纯Python实现,它将在系统没有当前Linux系统具有的crypt实现时使用(例如,http://www.akkadia.org/drepper/SHA-crypt.txt)。

请在此处查看PassLib的文档

passlib - 用于python的密码哈希库 - Google Project Hosting
https://code.google.com/p/passlib/

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

passlib-users - 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

答案 1 :(得分:7)

crypt的手册不精确(甚至误导)。 crypt与“MD5”,“SHA-256”或“SHA-512”标记使用的算法实际上是在这些基元上构建算法 。它们是password-based key derivation functions,使用哈希来执行key strengthening

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

Wikipedia article for crypt(3)提供了一个简短的解释,并指出了主要来源。 Linux和FreeBSD的手册页很差,但Solaris's有足够的信息不会误导(请点击crypt.conf(4)然后crypt_sha512和其他人的链接)。您还可以阅读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

答案 2 :(得分:5)

您的密码长度不一样,因为crypt() 输出是base64编码的,你使用hexdigest作为value1

而不是hexdigest,你应该尝试做类似

的事情
value1 = crypt_base64(hashlib.sha512(salt + password))

crypt_base64一样,bash implementation,最后一部分 doHash()功能。