> Java > java지도 시간 > PBKDF2를 사용하여 Java에서 비밀번호를 안전하게 해시하는 방법은 무엇입니까?

PBKDF2를 사용하여 Java에서 비밀번호를 안전하게 해시하는 방법은 무엇입니까?

Patricia Arquette
풀어 주다: 2024-12-29 12:38:14
원래의
773명이 탐색했습니다.

How to Securely Hash Passwords in Java Using PBKDF2?

Java에서 비밀번호를 안전하게 해시하는 방법

비밀번호 보안은 민감한 사용자 정보를 처리하는 모든 애플리케이션에서 매우 중요합니다. 해싱 비밀번호는 비밀번호가 해독되어 일반 텍스트로 저장되는 것을 방지하는 단방향 암호화 방법을 제공합니다.

시나리오:

저장용 비밀번호를 해시하려고 합니다. 데이터베이스, 추가를 위해 임의의 소금 추가 security.

해결책:

JRE(Java Runtime Environment)에는 PBKDF2(Password-Based Key Derivation Function 2)를 사용하여 비밀번호 해싱을 위한 내장 기능이 포함되어 있습니다. 이 방법은 강력한 비밀번호 보호를 제공하며 이를 구현하는 방법은 다음과 같습니다.

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는 비밀번호, 임의 솔트 및 비용 매개변수를 사용하여 해시를 계산합니다. 비용 매개변수는 해싱의 계산 강도를 제어합니다. 비용이 높을수록 해싱 속도는 느려지지만 보안은 강화됩니다.

보안을 더욱 강화하려면 다음과 같은 유틸리티 클래스를 사용하는 것이 좋습니다.

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);
    }
  }
}
로그인 후 복사

이 유틸리티 클래스는 비밀번호 해싱(hash) 및 사용자 인증(authenticate)을 위한 메소드를 제공합니다. 이는 사용자 정의 가능한 계산 비용 매개변수를 사용하고 추가 보호를 위해 무작위 솔트를 통합합니다. 이 유틸리티를 활용하면 Java 애플리케이션에서 비밀번호를 안전하게 저장하고 확인할 수 있습니다.

위 내용은 PBKDF2를 사용하여 Java에서 비밀번호를 안전하게 해시하는 방법은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
저자별 최신 기사
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿