Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
PasswordHasherPBKDF2.java
Go to the documentation of this file.
1package dev.rafex.ether.crypto.password;
2
3/*-
4 * #%L
5 * ether-crypto
6 * %%
7 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
8 * %%
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 * #L%
27 */
28
29import java.security.MessageDigest;
30import javax.crypto.SecretKeyFactory;
31import javax.crypto.spec.PBEKeySpec;
32import java.util.Arrays;
33
34/**
35 * PBKDF2-HMAC-SHA256 password hasher ported from Kiwi and HouseDB.
36 */
37public final class PasswordHasherPBKDF2 implements PasswordHasher {
38
39 private static final String ALGORITHM = "PBKDF2WithHmacSHA256";
40
41 private final int derivedKeyBytes;
42
43 /**
44 * Creates a new PBKDF2 password hasher.
45 *
46 * @param derivedKeyBytes The number of bytes for the derived key (minimum 16).
47 * @throws IllegalArgumentException if {@code derivedKeyBytes} is less than 16.
48 */
49 public PasswordHasherPBKDF2(final int derivedKeyBytes) {
50 if (derivedKeyBytes < 16) {
51 throw new IllegalArgumentException("derivedKeyBytes demasiado pequeño");
52 }
53 this.derivedKeyBytes = derivedKeyBytes;
54 }
55
56 /**
57 * Verifies a password against an expected hash using PBKDF2-HMAC-SHA256.
58 *
59 * @param password The password to verify.
60 * @param salt The random salt bytes.
61 * @param iterations The number of PBKDF2 iterations.
62 * @param expectedHash The expected hash bytes.
63 * @return {@code true} if the password matches the expected hash, {@code false} otherwise.
64 */
65 @Override
66 public boolean verify(final char[] password, final byte[] salt, final int iterations, final byte[] expectedHash) {
67 if (password == null || salt == null || expectedHash == null || iterations <= 0) {
68 return false;
69 }
70
71 final var derivedKey = derive(password, salt, iterations, expectedHash.length);
72 try {
73 return MessageDigest.isEqual(derivedKey, expectedHash);
74 } finally {
75 Arrays.fill(derivedKey, (byte) 0);
76 }
77 }
78
79 /**
80 * Hashes a password using PBKDF2-HMAC-SHA256.
81 *
82 * @param password The password to hash.
83 * @param salt The random salt bytes.
84 * @param iterations The number of PBKDF2 iterations.
85 * @return The resulting {@code PasswordHash}.
86 * @throws IllegalArgumentException if password is null, salt is null or empty, or iterations is not positive.
87 */
88 @Override
89 public PasswordHash hash(final char[] password, final byte[] salt, final int iterations) {
90 if (password == null) {
91 throw new IllegalArgumentException("password no puede ser null");
92 }
93 if (salt == null || salt.length == 0) {
94 throw new IllegalArgumentException("salt no puede ser null o vacio");
95 }
96 if (iterations <= 0) {
97 throw new IllegalArgumentException("iterations debe ser mayor que cero");
98 }
99
100 final var derivedKey = derive(password, salt, iterations, derivedKeyBytes);
101 return new PasswordHash(derivedKey, salt, iterations);
102 }
103
104 private static byte[] derive(final char[] password, final byte[] salt, final int iterations,
105 final int outLenBytes) {
106 try {
107 final var spec = new PBEKeySpec(password, salt, iterations, outLenBytes * 8);
108 final var secretKeyFactory = SecretKeyFactory.getInstance(ALGORITHM);
109 return secretKeyFactory.generateSecret(spec).getEncoded();
110 } catch (final Exception e) {
111 throw new IllegalStateException("PBKDF2 derivation failed", e);
112 }
113 }
114}
boolean verify(final char[] password, final byte[] salt, final int iterations, final byte[] expectedHash)
Verifies a password against an expected hash using PBKDF2-HMAC-SHA256.
PasswordHasherPBKDF2(final int derivedKeyBytes)
Creates a new PBKDF2 password hasher.
PasswordHash hash(final char[] password, final byte[] salt, final int iterations)
Hashes a password using PBKDF2-HMAC-SHA256.
Contract for password hashing and verification.
Password hashing and verification primitives for Ether.
record PasswordHash(byte[] hash, byte[] salt, int iterations)
Immutable password hash material.