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
So that the Python code returns the Keycloak hash, the salt must be Base64 decoded and the key size must be specified in bytes instead of bits: