Rumah >Java >javaTutorial >Bagaimana untuk Mencincang Kata Laluan dengan Selamat di Java Menggunakan PBKDF2?

Bagaimana untuk Mencincang Kata Laluan dengan Selamat di Java Menggunakan PBKDF2?

Patricia Arquette
Patricia Arquetteasal
2024-12-29 12:38:14807semak imbas

How to Securely Hash Passwords in Java Using PBKDF2?

Cara Mencincang Kata Laluan dengan Selamat di Java

Menjaga kata laluan adalah penting dalam mana-mana aplikasi yang mengendalikan maklumat pengguna yang sensitif. Kata laluan pencincangan menyediakan kaedah penyulitan sehala yang menghalang kata laluan daripada dinyahsulit dan disimpan dalam teks biasa.

Senario:

Anda ingin mencincang kata laluan untuk penyimpanan dalam pangkalan data, menambah garam rawak untuk tambahan keselamatan.

Penyelesaian:

Java Runtime Environment (JRE) termasuk kemudahan terbina dalam untuk pencincangan kata laluan menggunakan PBKDF2 (Fungsi Derivasi Kunci Berasaskan Kata Laluan 2). Kaedah ini menawarkan perlindungan kata laluan yang mantap, dan berikut ialah cara untuk melaksanakannya:

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 mengambil kata laluan, garam rawak dan parameter kos untuk mengira cincang. Parameter kos mengawal keamatan pengiraan pencincangan, dengan kos yang lebih tinggi menyebabkan pencincangan yang lebih perlahan tetapi keselamatan yang lebih kukuh.

Untuk meningkatkan lagi keselamatan, pertimbangkan untuk menggunakan kelas utiliti seperti ini:

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);
    }
  }
}

Kelas utiliti ini menyediakan kaedah untuk mencincang kata laluan (cincang) dan mengesahkan pengguna (mengesah). Ia menggunakan parameter kos pengiraan yang boleh disesuaikan dan menggabungkan garam rawak untuk perlindungan tambahan. Dengan menggunakan utiliti ini, anda boleh menyimpan dan mengesahkan kata laluan dengan selamat dalam aplikasi Java anda.

Atas ialah kandungan terperinci Bagaimana untuk Mencincang Kata Laluan dengan Selamat di Java Menggunakan PBKDF2?. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn