2

My use-case is that I need to migrate some passwords from Keycloak to Django. My sample password on Keycloak is 'qwerty123'. The Corresponding Hash Generated In Keycloak DB is this:

{"value":"PQzhC1QBMBHY/wBUZB4iWV0jtJHmFoHaGBgps9GGrIAeLSIQqzteNquvTgbuooPnltGB6EBpu3f+itYV8VMdcw==","salt":"ORBgqQI7lghE24ggYg+14A==","additionalParameters":{}}

However, if I paste this directly into the DB in the format specified by Django's Password Hashers, i.e,

pbkdf2_sha265$27500$ORBgqQI7lghE24ggYg+14A==$PQzhC1QBMBHY/wBUZB4iWV0jtJHmFoHaGBgps9GGrIAeLSIQqzteNquvTgbuooPnltGB6EBpu3f+itYV8VMdcw==

I get a password mismatch error. I tried to retrace the password hasher in Python, and found that it is generating a different hash from the one that I am inserting. I did:

   ...: from django.utils.crypto import pbkdf2 
   ...: import hashlib 
   ...: import base64 
   ...:  
   ...: secret = 'qwerty123' 
   ...: salt = 'ORBgqQI7lghE24ggYg+14A==' 
   ...: iterations = 27500 
   ...: digest = hashlib.sha256 
   ...: pwd_hash = pbkdf2_hmac(hashlib.sha256, secret, salt , iterations, 512)
   ...: pwd_hash = base64.b64encode(pwd_hash).decode('ascii').strip()
   ...: print(pwd_hash)

And The Output I got here was:

'yD/EY8mV0OPx0qv5ZGJDq1hyJ+QhIc8KTnITO1rDgzmVJwe/xM6by5VCyG84C9fBILX5UGNQwHGmYYqhDY1Ww2xoHjOXg/WrHlwEn+PU32/lJ4OwepbffPV/kHo+9Y1wxSFdx8zIiw992yIuh9d0A70u7822WFZJDcym1WSQZq/YMa6F+xUxMhEakHGQYth5CPsyCxWkQu1YgiM3KjRty/jfL7r8m+5f6PgBEKBYu0dtYc8QjWU+vSR8Nexz8EwHjIAhhA2iL2zGez+EfGLInDlON+QwffevKEQJnkld2gBp7Liz1Bd9rZjg0smy72nxFiR5gP+ZCTVEmS3e3bMnIGNGegrI6XxitdK2KIeSO+YfTFqVEF5zoNdjE5cx31TS1svcMAcf4uedJo+kARBw7oNOvACVrHMBUpmT5Vc+eaf/is8lzz6xEP97dfsiHZoegW2wJpWYl762NaVthKc1mThEblNsXTqrZRjH0OE6MXInpiwq+mn4TSt8epFMnYKfo5i6ektvbrwS3kJnU2wqJ7XP9lZH3Q7Lw7P2X8B/uaBhyePnJO2L84yqqfforwim8cYOWotdz30V7m2/xLukJiWWpK40ivkaGErzpbO1j4mMUYR+bsY4Fu3KD7TNLlJasDzPo7EIw1AwueS/k+n9Ucu2PBqf/WARKeYWbL3Yit0='

I also tried recreating the django password hasher code:

   ...: hash = pbkdf2(secret, salt, iterations, digest=digest) 
   ...: hash = base64.b64encode(hash).decode('ascii').strip()     
   ...: print(hash)                                                                                                                

Output for which is:

'yD/EY8mV0OPx0qv5ZGJDq1hyJ+QhIc8KTnITO1rDgzk='

Which is nowhere even close to the hash that Keycloak generated, can someone tell me what mistake am I making exactly? Any help will be appreciated.

I tried to check if Keycloak was doing some post processing before storing into the DB, so I checked this hash on 8gwifi.org . And it generates a hash identical to the one stored on Keycloak. So Python should also be able to arrive at the same hash. Screenshot From 8gwifi

4

1 回答 1

1

为了让 Python 代码返回 Keycloak 哈希,盐必须经过 Base64 解码,并且密钥大小必须以字节而不是位为单位指定:

import base64 
 
digest = 'sha256'
secret = b'qwerty123' 
salt = base64.b64decode(b'ORBgqQI7lghE24ggYg+14A==')  # Fix 1: Base64 decode to get the salt
iterations = 27500 
keysize = 512//8 # Fix 2: Pass key size in bytes

# hashlib -------------------------------------------------------

import hashlib 
      
pwd_hash = hashlib.pbkdf2_hmac(digest, secret, salt, iterations, keysize)
pwd_hash = base64.b64encode(pwd_hash).decode('ascii')

assert pwd_hash == 'PQzhC1QBMBHY/wBUZB4iWV0jtJHmFoHaGBgps9GGrIAeLSIQqzteNquvTgbuooPnltGB6EBpu3f+itYV8VMdcw=='
print('Hashes match')

# PyCryptodome --------------------------------------------------

from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256

pwd_hash2 = PBKDF2(secret, salt, keysize, iterations, hmac_hash_module=SHA256)
pwd_hash2 = base64.b64encode(pwd_hash2).decode('ascii')

assert pwd_hash2 == 'PQzhC1QBMBHY/wBUZB4iWV0jtJHmFoHaGBgps9GGrIAeLSIQqzteNquvTgbuooPnltGB6EBpu3f+itYV8VMdcw=='
print('Hashes match')
于 2021-08-13T08:20:23.437 回答