python - 将 python 2 检查哈希转换为 python 3

标签 python python-3.x hash python-unicode

我需要将用户及其密码从 python 2 系统转移到 python 3 系统。

PW 哈希看起来像这样:

PBKDF2$sha256$10000$KlCW+ewerd19fS9f$l+5LgvcWTzghtz77086MSVG+q5z2Lij

在 python 2 系统中,我使用这些函数来检查哈希值:

def check_hash(password, hash_):
    """Check a password against an existing hash."""
    if isinstance(password, unicode):
        password = password.encode('utf-8')
    algorithm, hash_function, cost_factor, salt, hash_a = hash_.split('$')
    assert algorithm == 'PBKDF2'
    hash_a = b64decode(hash_a)
    hash_b = pbkdf2_bin(password, salt, int(cost_factor), len(hash_a),
                        getattr(hashlib, hash_function))
    assert len(hash_a) == len(hash_b)  # we requested this from pbkdf2_bin()
    # Same as "return hash_a == hash_b" but takes a constant time.
    # See http://carlos.bueno.org/2011/10/timing.html
    diff = 0
    for char_a, char_b in izip(hash_a, hash_b):
        diff |= ord(char_a) ^ ord(char_b)
    return diff == 0

还有这个:

_pack_int = Struct('>I').pack

def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
    """Returns a binary digest for the PBKDF2 hash algorithm of `data`
    with the given `salt`.  It iterates `iterations` time and produces a
    key of `keylen` bytes.  By default SHA-1 is used as hash function,
    a different hashlib `hashfunc` can be provided.
    """
    hashfunc = hashfunc or hashlib.sha1
    mac = hmac.new(data, None, hashfunc)
    def _pseudorandom(x, mac=mac):
        h = mac.copy()
        h.update(x)
        return map(ord, h.digest())
    buf = []
    for block in xrange(1, -(-keylen // mac.digest_size) + 1):
        rv = u = _pseudorandom(salt + _pack_int(block))
        for i in xrange(iterations - 1):
            u = _pseudorandom(''.join(map(chr, u)))
            rv = starmap(xor, izip(rv, u))
        buf.extend(rv)
    return ''.join(map(chr, buf))[:keylen]

到目前为止我做了什么:

在将代码复制到我的 python 3 脚本后,我必须更改一些变量:

izip -> zip

我保留了 unicode:from past.builtins import unicode

我保留了 xrange:from past.builtins import xrange

现在我没有脚本错误,但是在执行脚本后我在这里遇到错误(在 pbkdf2_bin 函数中):

rv = u = _pseudorandom(salt + _pack_int(block))
TypeError: must be str, not bytes

所以我通过将 bytes 转换为 str 来修复它:

rv = u = _pseudorandom(salt + _pack_int(block).decode('utf-8'))

现在出现下一个错误(在 pbkdf2_bin 函数中):

h.update(x)
TypeError: Unicode-objects must be encoded before hashing

我还用正确的编码解决了这个问题:

h.update(x.encode('utf-8'))

下一个错误:

  File "C:\Users\User\Eclipse-Workspace\Monteurzimmer-Remastered\hash_passwords.py", line 123, in check_hash
    getattr(hashlib, hash_function))
  File "C:\Users\User\Eclipse-Workspace\Monteurzimmer-Remastered\pbkdf2.py", line 125, in pbkdf2_bin_old_2
    u = _pseudorandom(''.join(map(chr, u)))
TypeError: ord() expected string of length 1, but int found

_pseudorandom 的返回值存在问题(在 pbkdf2_bin 函数中)。它必须被转换,所以我修复了它:

可能是这里的问题

#return map(ord, h.digest()) # throws the error
#return map(int, h.digest()) # returns nothing (length 0)
#return list(map(ord, h.digest())) # throws the error
return list(map(int, h.digest())) # seems to work with the correct length

最后一个错误是在 check_hash 函数的末尾:

File "C:\Users\User\Eclipse-Workspace\Monteurzimmer-Remastered\hash_passwords.py", line 129, in check_hash
    diff |= ord(char_a) ^ ord(char_b)
TypeError: ord() expected string of length 1, but int found

for char_a, char_b in zip(hash_a, hash_b):
    diff |= ord(char_a) ^ ord(char_b)

char_a 是一个整数而 chat_b 不是。我能够通过将 char_a 转换为真正的 char 来解决这个问题:

for char_a, char_b in zip(hash_a, hash_b):
    diff |= ord(chr(char_a)) ^ ord(char_b)

终于没有报错了,但是提示我输入的密码错误, 所以某处是一个错误,因为我知道密码是正确的并且它适用于 python 2 应用程序。

编辑

有人提到 2to3 库,所以我尝试了一下。总而言之,它做了同样的事情,我已经做了,问题是一样的。

编辑赏金

总结一下。我上面发布的 2 个函数来自 python 2 并在 python 2 中工作。

这个散列:

PBKDF2$sha256$10000$r+Gy8ewTkE7Qv0V7$uqmgaPgpaT1RSvFPMcGb6cGaFAhjyxE9

这个密码是:Xs12'io!12

我可以在我的 python 2 应用程序上使用此密码正确登录。

现在我想在 python 3 中使用相同的两个函数,但即使我解决了所有错误,它仍然告诉我密码错误。

进口:

import hmac
import hashlib
from struct import Struct
from operator import xor
from itertools import izip, starmap

from base64 import b64encode, b64decode
import hashlib
from itertools import izip
from os import urandom
import random
import string

这些导入在 python 2 脚本中使用。

最佳答案

我想我成功了。我在 github 上找到了原始代码这帮助我创建了一个测试用例。在查看问题后,我遵循了您提出的解决方案,并将字节解码为 iso-8859-1 而不是 utf-8 并且它有效。

from struct import Struct
from base64 import b64decode
import hashlib
import hmac
from operator import xor
from itertools import starmap


_pack_int = Struct('>I').pack


def check_hash(password, hash_):
    """Check a password against an existing hash."""
    if isinstance(password, str):
        password = password.encode('utf-8')
    algorithm, hash_function, cost_factor, salt, hash_a = hash_.split('$')
    assert algorithm == 'PBKDF2'
    hash_a = b64decode(hash_a).decode('iso-8859-1')
    hash_b = pbkdf2_bin(password, salt, int(cost_factor), len(hash_a),
                        getattr(hashlib, hash_function))
    assert len(hash_a) == len(hash_b)  # we requested this from pbkdf2_bin()
    # Same as "return hash_a == hash_b" but takes a constant time.
    # See http://carlos.bueno.org/2011/10/timing.html
    diff = 0
    for char_a, char_b in zip(hash_a, hash_b):
        diff |= ord(char_a) ^ ord(char_b)
    return diff == 0


def pbkdf2_bin(data, salt, iterations=1000, keylen=24, hashfunc=None):
    """Returns a binary digest for the PBKDF2 hash algorithm of `data`
    with the given `salt`.  It iterates `iterations` time and produces a
    key of `keylen` bytes.  By default SHA-1 is used as hash function,
    a different hashlib `hashfunc` can be provided.
    """
    hashfunc = hashfunc or hashlib.sha1
    mac = hmac.new(data, None, hashfunc)
    def _pseudorandom(x, mac=mac):
        h = mac.copy()
        h.update(x)
        return list(map(ord, h.digest().decode('iso-8859-1')))
    buf = []
    for block in range(1, -(-keylen // mac.digest_size) + 1):
        myx = salt.encode('utf-8') + _pack_int(block)
        rv = u = _pseudorandom(myx)
        for i in range(iterations - 1):
            u = _pseudorandom(''.join(map(chr, u)).encode('iso-8859-1'))
            rv = starmap(xor, zip(rv, u))
        buf.extend(rv)
    return ''.join(map(chr, buf))[:keylen]


if __name__ == "__main__":
    print(check_hash('Xs12\'io!12', 'PBKDF2$sha256$10000$r+Gy8ewTkE7Qv0V7$uqmgaPgpaT1RSvFPMcGb6cGaFAhjyxE9'))

与其继续细化和维护这个脚本,我建议查看同一函数的 python3 实现。

引用资料

关于python - 将 python 2 检查哈希转换为 python 3,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54251824/

相关文章:

python - Spark 对 HashingTF 使用什么哈希函数以及如何复制它?

python - 默认情况下取消选择 Bokeh 工具

python - 根据日期和错误格式过滤 pandas 数据框

python - 如何定义一个函数以便可以在对象上调用它?

arrays - Perl:映射到列表的第一个元素

database - WebApp 密码管理 - 散列、加盐等

python - 从 XML python 中检索数据

python - 与 curl 一起工作,请求失败;如何修复我的请求代码?

python - 数字 1-5 的所有组合总和 <=9

image - 使用 python urllib 从 url 下载图像但收到 HTTP 错误 403 : Forbidden