Hashes en Java
Es posible crear hashes en Java usando las clases que vienen con el JDK:
- Para hashing rápido (checksums e integridad) usa
MessageDigest(MD5,SHA-1,SHA-256, etc.). - Para hashes de contraseñas usa un derivador de clave seguro como
PBKDF2(disponible en Java estándar víaSecretKeyFactory) y siempre combina con salt y muchas iteraciones.
Hash simple con MessageDigest (SHA-256)
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HashUtils {
public static String sha256Hex(String input) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-256");
byte[] hashed = md.digest(input.getBytes(StandardCharsets.UTF_8));
return bytesToHex(hashed);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("SHA-256 no disponible", e);
}
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}
// ejemplo de uso
public static void main(String[] args) {
String texto = "hola mundo";
System.out.println("SHA-256: " + sha256Hex(texto));
}
}
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class HashPassword {
public static String hashPassword(String password) {
try {
// Crear una instancia de MessageDigest para SHA-256
MessageDigest digest = MessageDigest.getInstance("SHA-256");
// Pasar la contraseña como bytes al digest
byte[] hash = digest.digest(password.getBytes());
// Convertir el hash a una cadena hexadecimal
StringBuilder hexString = new StringBuilder();
for (byte b : hash) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) hexString.append('0');
hexString.append(hex);
}
return hexString.toString();
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
}
Consideraciones:
- Usa
SHA-256(oSHA-512) en vez deMD5oSHA-1si buscas integridad; MD5/SHA-1 están rotos frente a colisiones. MessageDigestes parte dejava.security. No necesitas librerías externas.
Hash seguro para contraseñas: PBKDF2 (salt + iteraciones)
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
public class PasswordHash {
private static final String ALGO = "PBKDF2WithHmacSHA256";
private static final int ITERATIONS = 100_000; // ajustar según necesidades
private static final int KEY_LENGTH = 256; // bits
private static final int SALT_LENGTH = 16; // bytes
public static String generateSaltBase64() {
byte[] salt = new byte[SALT_LENGTH];
new SecureRandom().nextBytes(salt);
return Base64.getEncoder().encodeToString(salt);
}
public static String hashPasswordBase64(char[] password, byte[] salt) {
try {
PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH);
SecretKeyFactory skf = SecretKeyFactory.getInstance(ALGO);
byte[] hash = skf.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(hash);
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
throw new RuntimeException("Error al hashear password", e);
}
}
public static boolean verifyPassword(char[] attempt, byte[] salt, byte[] expectedHash) {
String attemptHashB64 = hashPasswordBase64(attempt, salt);
String expectedB64 = Base64.getEncoder().encodeToString(expectedHash);
// Comparación resistente a timing attacks:
return MessageDigest.isEqual(attemptHashB64.getBytes(), expectedB64.getBytes());
}
}
- Cuando el usuario se registra:
- Genera
saltcongenerateSaltBase64(). - Hashea la contraseña con
hashPasswordBase64(...). - Guarda en DB:
saltyhash(ambos en Base64), y también la info deiteraciones/algoritmo si quieres migrar más tarde.
- Genera
- Al verificar login:
- Recupera
saltyhashde la base de datos, calcula hash del intento y compara de forma segura.
- Recupera
Consideraciones:
PBKDF2WithHmacSHA256está disponible en Java 8+ (depende del proveedor, pero suele estar).- Ajusta
ITERATIONSsegún rendimiento y seguridad (más iteraciones = más lento para atacantes). - Para máxima seguridad se recomiendan algoritmos como Argon2, pero Argon2 requiere bibliotecas externas si tu JDK no lo soporta.
Consejos prácticos y de seguridad
- Para integridad/identificación:
SHA-256es una buena opción. - Para contraseñas: nunca uses SHA-256 directo; usa PBKDF2, scrypt o Argon2 con salt y muchas iteraciones.
- Nunca guardes contraseñas en texto plano. Guarda
salt+hash+ algoritmo/iteraciones. - Usa
SecureRandompara generar salt. - Para comparar hashes, usa
MessageDigest.isEqual(...)para evitar vulnerabilidades por timing. - Codifica bytes en hex o Base64 para almacenamiento legible.
- Evita reusar salts. Cada contraseña debe tener su salt único.
- Si necesitas un hash no reversible para tokens, puedes combinar valores con un secreto (HMAC). Java incluye
Macenjavax.cryptopara HMAC (HmacSHA256) sin librería externa.