Random Notes by agilob

JavaKeycloak password hashing

Jump to Section

This post doesn’t contain full context of the works performed, only benchmarking part

I had to test how number of iterations impacts login request time to KeyCloak and if or how we can improve it. After investigating a few other options I decided to check what’s the difference for password hashing times using default hashing mechanism in KeyCloak. I found and extracted parts of password hashing metchanism from KeyCloak to my repo, developed small parametrised JMH class and here are the results I found with this config:

@State(Scope.Benchmark)
@Fork(value = 3)
@Warmup(iterations = 1)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)
Benchmark                                (iteration)    (password)                    (salt)   Mode  Cnt       Score       Error  Units
PasswordHashingBenchmark.testPbkdf2Sha1            1      password  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5  153588.532 ± 38694.974  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1            1      password  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5  152186.035 ± 40803.380  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1            1  password123!  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5  152086.927 ± 39779.043  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1            1  password123!  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5  152281.627 ± 40021.489  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1         1000      password  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5     208.764 ±     0.549  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1         1000      password  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5     209.461 ±     0.202  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1         1000  password123!  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5     208.324 ±     0.605  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1         1000  password123!  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5     210.098 ±     0.561  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        15000      password  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5      13.764 ±     0.029  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        15000      password  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5      13.771 ±     0.048  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        15000  password123!  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5      13.828 ±     0.037  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        15000  password123!  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5      13.789 ±     0.078  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        27500      password  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5       7.534 ±     0.024  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        27500      password  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5       7.521 ±     0.013  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        27500  password123!  Hy9X2DNQNOVYOictvV0wrA==  thrpt    5       7.525 ±     0.018  ops/s
PasswordHashingBenchmark.testPbkdf2Sha1        27500  password123!  rqdzpZGc2XqFgjMeCRVZLg==  thrpt    5       7.514 ±     0.069  ops/s

and then just to see how it compares in average time mode I rerun it with the following config:

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@Fork(value = 3)
@Warmup(iterations = 1)
@Measurement(iterations = 5, time = 1, timeUnit = TimeUnit.SECONDS)

and the results are:

Benchmark                                (iteration)    (password)                    (salt)   Mode  Cnt       Score       Error  Units
PasswordHashingBenchmark.testPbkdf2Sha1            1      password  Hy9X2DNQNOVYOictvV0wrA==   avgt   15      ≈ 10⁻⁵               s/op
PasswordHashingBenchmark.testPbkdf2Sha1            1      password  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15      ≈ 10⁻⁵               s/op
PasswordHashingBenchmark.testPbkdf2Sha1            1  password123!  Hy9X2DNQNOVYOictvV0wrA==   avgt   15      ≈ 10⁻⁵               s/op
PasswordHashingBenchmark.testPbkdf2Sha1            1  password123!  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15      ≈ 10⁻⁵               s/op
PasswordHashingBenchmark.testPbkdf2Sha1         1000      password  Hy9X2DNQNOVYOictvV0wrA==   avgt   15       0.005 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1         1000      password  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15       0.005 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1         1000  password123!  Hy9X2DNQNOVYOictvV0wrA==   avgt   15       0.005 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1         1000  password123!  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15       0.005 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        15000      password  Hy9X2DNQNOVYOictvV0wrA==   avgt   15       0.072 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        15000      password  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15       0.072 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        15000  password123!  Hy9X2DNQNOVYOictvV0wrA==   avgt   15       0.072 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        15000  password123!  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15       0.072 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        27500      password  Hy9X2DNQNOVYOictvV0wrA==   avgt   15       0.132 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        27500      password  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15       0.133 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        27500  password123!  Hy9X2DNQNOVYOictvV0wrA==   avgt   15       0.132 ±     0.001   s/op
PasswordHashingBenchmark.testPbkdf2Sha1        27500  password123!  rqdzpZGc2XqFgjMeCRVZLg==   avgt   15       0.133 ±     0.001   s/op

Source code for these tests is avilable here