Maison >Java >javaDidacticiel >Comment hacher en toute sécurité des mots de passe en Java à l'aide de PBKDF2 ?

Comment hacher en toute sécurité des mots de passe en Java à l'aide de PBKDF2 ?

Patricia Arquette
Patricia Arquetteoriginal
2024-12-29 12:38:14773parcourir

How to Securely Hash Passwords in Java Using PBKDF2?

Comment hacher des mots de passe en toute sécurité en Java

La sécurisation des mots de passe est cruciale dans toute application qui gère des informations utilisateur sensibles. Le hachage des mots de passe fournit une méthode de cryptage unidirectionnelle qui empêche les mots de passe d'être déchiffrés et stockés en texte brut.

Scénario :

Vous souhaitez hacher les mots de passe pour les stocker dans un base de données, en ajoutant un sel aléatoire pour plus sécurité.

Solution :

L'environnement d'exécution Java (JRE) comprend une fonction intégrée pour le hachage de mot de passe à l'aide de PBKDF2 (fonction de dérivation de clé basée sur le mot de passe 2). Cette méthode offre une protection par mot de passe robuste, et voici comment la mettre en œuvre :

SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
KeySpec spec = new PBEKeySpec("password".toCharArray(), salt, 65536, 128);
SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
byte[] hash = f.generateSecret(spec).getEncoded();
Base64.Encoder enc = Base64.getEncoder();
System.out.printf("salt: %s%n", enc.encodeToString(salt));
System.out.printf("hash: %s%n", enc.encodeToString(hash));

PBKDF2 prend un mot de passe, un sel aléatoire et un paramètre de coût pour calculer le hachage. Le paramètre de coût contrôle l'intensité de calcul du hachage, des coûts plus élevés entraînant un hachage plus lent mais une sécurité plus forte.

Pour améliorer encore la sécurité, envisagez d'utiliser une classe d'utilitaire comme celle-ci :

import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;

/**
 * Utility class for PBKDF2 password authentication
 */
public final class PasswordAuthentication {

  // Constants
  public static final String ID = "$";
  public static final int DEFAULT_COST = 16;
  private static final String ALGORITHM = "PBKDF2WithHmacSHA1";
  private static final int SIZE = 128;
  private static final Pattern layout = Pattern.compile("\\$(\d\d?)\$(.{43})");

  // Instance variables
  private final SecureRandom random;
  private final int cost;

  /**
   * Constructor with default cost
   */
  public PasswordAuthentication() {
    this(DEFAULT_COST);
  }

  /**
   * Constructor with specified cost
   *
   * @param cost the exponential computational cost of hashing a password, 0 to 30
   */
  public PasswordAuthentication(int cost) {
    iterations(cost); // Validate cost
    this.cost = cost;
    this.random = new SecureRandom();
  }

  private static int iterations(int cost) {
    if ((cost < 0) || (cost > 30)) {
      throw new IllegalArgumentException("cost: " + cost);
    }
    return 1 << cost;
  }

  /**
   * Hash a password for storage
   *
   * @return a secure authentication token to be stored for later authentication
   */
  public String hash(char[] password) {
    byte[] salt = new byte[SIZE / 8];
    random.nextBytes(salt);
    byte[] dk = pbkdf2(password, salt, 1 << cost);
    byte[] hash = new byte[salt.length + dk.length];
    System.arraycopy(salt, 0, hash, 0, salt.length);
    System.arraycopy(dk, 0, hash, salt.length, dk.length);
    Base64.Encoder enc = Base64.getUrlEncoder().withoutPadding();
    return ID + cost + '$' + enc.encodeToString(hash);
  }

  /**
   * Authenticate with a password and a stored password token
   *
   * @return true if the password and token match
   */
  public boolean authenticate(char[] password, String token) {
    Matcher m = layout.matcher(token);
    if (!m.matches()) {
      throw new IllegalArgumentException("Invalid token format");
    }
    int iterations = iterations(Integer.parseInt(m.group(1)));
    byte[] hash = Base64.getUrlDecoder().decode(m.group(2));
    byte[] salt = Arrays.copyOfRange(hash, 0, SIZE / 8);
    byte[] check = pbkdf2(password, salt, iterations);
    int zero = 0;
    for (int idx = 0; idx < check.length; ++idx) {
      zero |= hash[salt.length + idx] ^ check[idx];
    }
    return zero == 0;
  }

  private static byte[] pbkdf2(char[] password, byte[] salt, int iterations) {
    KeySpec spec = new PBEKeySpec(password, salt, iterations, SIZE);
    try {
      SecretKeyFactory f = SecretKeyFactory.getInstance(ALGORITHM);
      return f.generateSecret(spec).getEncoded();
    } catch (NoSuchAlgorithmException ex) {
      throw new IllegalStateException("Missing algorithm: " + ALGORITHM, ex);
    } catch (InvalidKeySpecException ex) {
      throw new IllegalStateException("Invalid SecretKeyFactory", ex);
    }
  }
}

Cette classe d'utilitaires fournit des méthodes pour hacher les mots de passe (hachage) et authentifier les utilisateurs (authentifier). Il utilise un paramètre de coût de calcul personnalisable et intègre un sel aléatoire pour une protection supplémentaire. En utilisant cet utilitaire, vous pouvez stocker et vérifier en toute sécurité les mots de passe dans votre application Java.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn