2014-01-11 29 views
9

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

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

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

下面的代码验证当我调用Python的系统crypt(即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个字符长,所以事情是这两个implmentations之间的路要走......

下面是对于那些不想运行的代码谁输出:

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)) 
+0

是您的密码(不包括数字)故意用葡萄牙语表示的还是一个尴尬的巧合?只是好奇':)' – JMCF125

+1

这个密码是一个真正的密码,从真正的被盗密码fwiw的大型数据库中提取。 – MountainX

+0

我明白了。如果没有盐,用葡萄牙语小字典制作的彩虹桌确实很容易破解。顺便说一句,+1,这是一个有趣的问题,即使我不使用Python。 – JMCF125

回答

11

下面是解决方案。在另一个问题中还有更多的细节:Python implementation of sha512_crypt.c它显示passlib的后端包含sha512_crypt的纯Python实现(如果crypt.crypt()在OS上不可用,则调用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 - 密码哈希库蟒蛇 - 谷歌项目托管
https://code.google.com/p/passlib/

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

passlib-users - Google Groups
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地穴 - Passlib V1.6.2文档
http://pythonhosted.org/passlib/lib/passlib.hash.sha512_crypt.html#passlib.hash.sha512_crypt

4

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

相反hexdigest的,你应该尝试做这样的事情

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

crypt_base64bash implementationdoHash()功能最后一部分。

+0

很好的建议,但你的答案是不够的。你能基于我的简单例子提供可用的Python代码吗?我用base64编码更新了我的答案,但是这两个结果仍然不匹配。 – MountainX

+1

@MountainX你错误地认为bash脚本实现了“标准”b64编码。通过从base64调用b64encode不能松懈地实现crypt_base64。 – 2014-01-12 08:07:02

+0

谢谢你的回答。它帮助我走上正轨。看到我的答案是“准备运行”的解决方案。 – MountainX

7

crypt的手册不准确(甚至有误导性)。具有“MD5”,“SHA-256”或“SHA-512”标记的crypt所使用的算法实际上是在这些基元上的算法构建的。他们是password-based key derivation functions,使用散列执行key strengthening

A 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

+0

谢谢你的回答。它帮助我走上正轨。 – MountainX

相关问题