在 .NET 中验证 Python Passlib 生成的 PBKDF2 SHA512 哈希

Verify Python Passlib generated PBKDF2 SHA512 Hash in .NET

我正在迁移一个使用 Passlib 1.6.2 生成密码哈希的平台。加密密码的代码是(以轮次的默认值调用散列):

from passlib.hash import pbkdf2_sha512 as pb

def hash(cleartext, rounds=10001):
    return pb.encrypt(cleartext, rounds=rounds)

输出格式如下(对于密码“Patient3”(无引号)):

$pbkdf2-sha512001[=12=]dr7v7eWUmptrfW.9z6HkA$w9j9AMVmKAP17OosCqDxDv2hjsvzlLpF8Rra8I7p/b5746rghZ8WrgEjDpvXG5hLz1UeNLzgFa81Drbx2b7.hg

和“Testing123”

$pbkdf2-sha512001ZuTslYKAYDQGiPkfA.B8A$ChsEXEjanEToQcPJiuVaKk0Ls3n0YK7gnxsu59rxWOawl/iKgo0XSWyaAfhFV0.Yu3QqfehB4dc7yGGsIW.ARQ

我可以看到代表:

Passlib 算法在their site 上定义并读取:

All of the pbkdf2 hashes defined by passlib follow the same format, $pbkdf2-digest$rounds$salt$checksum.

$pbkdf2-digest$ is used as the Modular Crypt Format identifier ($pbkdf2-sha256$ in the example). digest - this specifies the particular cryptographic hash used in conjunction with HMAC to form PBKDF2’s pseudorandom function for that particular hash (sha256 in the example). rounds - the number of iterations that should be performed. this is encoded as a positive decimal number with no zero-padding (6400 in the example). salt - this is the adapted base64 encoding of the raw salt bytes passed into the PBKDF2 function. checksum - this is the adapted base64 encoding of the raw derived key bytes returned from the PBKDF2 function. Each scheme uses the digest size of its specific hash algorithm (digest) as the size of the raw derived key. This is enlarged by approximately 4/3 by the base64 encoding, resulting in a checksum size of 27, 43, and 86 for each of the respective algorithms listed above.

我发现 passlib.net 看起来有点像废弃的测试版,它使用“$6$”作为算法。我无法让它验证密码。我尝试将算法更改为 $6$,但我怀疑实际上也会更改盐。

我也尝试过使用 PWDTK 和不同的 salt 和 hash 值,但可能是我错误地拆分了影子密码,或者在一些我不应该出现的地方提供了 $。

有什么方法可以根据 .NET 中的哈希值验证密码吗?或者不涉及 Python 代理或让用户重新提供密码的另一种解决方案?

通过将密码传递给 PBKDF HMAC-SHA-256 哈希方法来验证哈希,然后将生成的哈希与保存的哈希部分进行比较,从 Base64 版本转换回来。

将哈希保存为二进制,然后分离哈希 使用UTF-8编码将密码转换为二进制 PBKDF2、HMAC、SHA-256(toBinary(密码、盐、10001)==散列 密码:"Patient3"

$pbkdf2 - sha512$10001$0dr7v7eWUmptrfW.9z6HkA$w9j9AMVmKAP17OosCqDxDv2hjsvzlLpF8Rra8I7p/b5746rghZ8WrgEjDpvXG5hLz1UeNLzgFa81Drbx2b7.hg

分解为(将字符串转换为标准 Base64(将“.”更改为“+”并添加尾随“=”填充:

pbkdf2 - sha512
10001
0dr7v7eWUmptrfW+9z6HkA==
w9j9AMVmKAP17OosCqDxDv2hjsvzlLpF8Rra8I7p/b5746rghZ8WrgEjDpvXG5hLz1UeNLzgFa81Drbx2b7+hg==

解码为十六进制:

D1DAFBBFB796526A6DADF5BEF73E8790
C3D8FD00C5662803F5ECEA2C0AA0F10EFDA18ECBF394BA45F11ADAF08EE9FDBE7BE3AAE0859F16AE01230E9BD71B984BCF551E34BCE015AF350EB6F1D9BEFE86

这是有道理的:16 字节(128 位)salt 和 64 字节(512 位)SHA-512 哈希。

使用 UTF-8 将 "Patient3" 转换为二进制数组 将盐从修改后的 BASE64 编码转换为 16 字节二进制数组 使用迭代计数 od 10001 使用带有 SHA-512

的 HMAC 将其提供给 PBKDF2

我明白了

C3D8FD00C5662803F5ECEA2C0AA0F10EFDA18ECBF394BA45F11ADAF08EE9FDBE7BE3AAE0859F16AE01230E9BD71B984BCF551E34BCE015AF350EB6F1D9BEFE86

其中,当 Base64 编码时,将“+”字符替换为“.”并删除尾随的 '=' 字符 returns: w9j9AMVmKAP17OosCqDxDv2hjsvzlLpF8Rra8I7p/b5746rghZ8WrgEjDpvXG5hLz1UeNLzgFa81Drbx2b7.hg

我使用 zaph 的逻辑并使用 SO answer. I have put the code on GitHub 上 JimmiTh 的代码快速组合了一个 .NET 实现(这应该不是生产就绪的)。它似乎适用于我们用户群中的多个示例。

正如 zaph 所说,逻辑是:

  1. 拆分哈希以找到迭代计数、salt 和哈希密码。 (我假设了算法,但你会验证它)。您将有一个包含 5 个值的数组,其中包含 [0] - 无,[1] - 算法,[2] - 迭代,[3] - 盐和 [4] - 哈希
  2. 通过替换任何“.”将盐转化为标准的 Base64 编码带有“+”字符并附加“==”的字符。
  3. 将密码、salt 和迭代计数传递给 PBKDF2-HMAC-SHA512 生成器。
  4. 通过将任何“+”字符替换为“.”来转换回原始 base64 格式字符并去除尾随的“==”。
  5. 将原始散列(拆分字符串中的元素 4)与此转换后的值进行比较,如果它们相等,则表示匹配。