Tech Notes

KeyCloakのパスワードハッシュの実装について調べてみた

多分あんまり必要とされない知識だと思うがメモ。動機としては以下のような理由で調べていた。

  • KeyCloakのユーザのパスワードを、KeyCloakの外にある自家製システムからREST APIで直接更新したい。
  • 更新失敗時に時間を置いて後で再試行出来るようにしたいが、後の再試行のために生パスワードを保存したくない。
  • 自家製システム内でハッシュ化を行った上でハッシュの方をKeyCloakに渡す形にできないか?これなら再試行が必要な場合も生パスワードでなくハッシュを保存すれば良くなる。

以下、「パスワードハッシュ」「ソルト」などの基本的知識はある前提で書いていく。

KeyCloakのAdmin Consoleで適当なユーザのCredentials→Show Dataを確認すると、自分の建てた環境(KeyCloak 11.0.3)では

algorithm: pbkdf2-sha256
hashIterations: 27500

という結果が出る。

さらにちょっとDBを直接覗いて「credential」テーブルの情報を見てみると、各ユーザーに対応して以下のようなデータが出てくる。

secret: {
  "value":"oVD+6dGK/gL+ydKy9DxBIuD1rolSCLjmtW1rq9RxHtOJOf0mjeDajEB+TXMiPPlYiYhEHznD2nahyeVlrRJkPw==",
  "salt":"pQE+OvqmkNmzEvIhjckUfw=="
}
credential_data: {
  "hashIterations":27500,
  "algorithm":"pbkdf2-sha256"
}

credential_dataの方が「Show Data」で表示されている情報のようだ。そしてsecretにパスワードハッシュなどの情報が収録されているらしい。

問題は、生パスワードとソルトからどうすれば自力でsecretのvalueの情報を生成できるのかだ。結論から言えば、例えばPHPの場合以下のように行うことが出来る。

base64_encode(
  hash_pbkdf2(
    "sha256",  // algorithm
    "password",  // password
    base64_decode("pQE+OvqmkNmzEvIhjckUfw=="),  // salt
    27500,  // iteration
    64,  // byte
    TRUE  // binary
  )
);

valueの値はpdkbf2とsha256で64バイトバイナリ形式の鍵を出力し、それをbase64でエンコードしたものらしい。PHPならばhash_pbkdf2という関数があるのでそれをそのまま利用できる。最後にバイナリフラグをTRUEにしないとバイナリではなく16進数文字列になってしまうので気を付ける。

ちなみにsaltは16バイトのバイナリをbase64エンコードしたものだった。もっとも自分でパスワードハッシュを生成する場合、ソルトは最後にbase64かける所だけ守れば後は自由に生成して良さそうだが。iterationもKeyCloakのデフォが27500なだけで、そこは縛られている訳ではないはず。

こうして生成したハッシュとソルトをAdmin REST APIで/auth/admin/realms/{realm}/users/{id}/reset-passwordエンドポイントに以下のような形で投げればパスワードは更新できるはずである。詳細はこの辺とか。

{
  "type": "password",
  "temporary": false,
  "algorithm": "pbkdf2-sha256",
  "hashIterations": 27500,
  "hashedSaltedValue":"oVD+6dGK/gL+ydKy9DxBIuD1rolSCLjmtW1rq9RxHtOJOf0mjeDajEB+TXMiPPlYiYhEHznD2nahyeVlrRJkPw==",
  "salt":"pQE+OvqmkNmzEvIhjckUfw=="
}

まとめ

  1. 適当にソルトを生成(KeyCloakに倣えばランダムバイナリ16バイト)
  2. pdkbf2&sha256で64バイトの鍵をバイナリ形式で生成
  3. 鍵とソルトをそれぞれbase64エンコード
  4. reset-passwordエンドポイントに所定の形式で投げる

でパスワード更新可能。

余談だが、KeyCloakのデフォルトではハッシュ関数はPBKDF2のみをサポートしているらしい。ソースはここ。なので標準の状態だと上記のalgorithmにはpbkdf2-sha256以外使えないものと思われる。ただプラグインで自分のハッシュ関数を追加できるらしくて、bcryptなどはgithubにプラグインのリポジトリもある。KeyCloakプラグインの追加方法は調べるのが面倒だったので調べていない。

参考にしたURL

https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_resetpassword

https://qiita.com/ogawa_pro/items/ec25ecf90f2b0fdeff37

https://www.keycloak.org/docs/latest/server_admin/index.html#password-policy-types

https://stackoverrun.com/ja/q/9346466

https://dev.to/carnewal/import-existing-users-with-bcrypt-hashed-passwords-in-keycloak-17oo

https://github.com/leroyguillaume/keycloak-bcrypt