Java에서 비밀번호를 어떻게 해시합니까?


176

데이터베이스에 저장하기 위해 비밀번호를 해시해야합니다. Java로 어떻게 할 수 있습니까?

나는 일반 텍스트 암호를 가져 와서 임의의 소금을 추가 한 다음 소금과 해시 된 암호를 데이터베이스에 저장하기를 바랐습니다.

그런 다음 사용자가 로그인하고 싶을 때 제출 한 비밀번호를 가져 와서 계정 정보에서 임의의 소금을 추가하고 해시하여 저장된 해시 비밀번호와 계정 정보가 동일한 지 확인할 수 있습니다.


11
@YGL은 오늘날 GPU 공격이 너무 저렴하여 실제로는 재조합이 아니며 SHA 제품군은 실제로 소금으로도 암호 해싱 (너무 빠름)에 매우 나쁜 선택입니다. bcrypt, scrypt 또는 PBKDF2 사용
Eran Medan

11
이 질문이 왜 닫혔습니까? 이것은 실제 엔지니어링 문제에 대한 질문이며 그 답은 매우 중요합니다. OP는 라이브러리를 요구하지 않고 엔지니어링 문제를 해결하는 방법을 요구하고 있습니다.
stackoverflowuser2010

12
놀랍습니다. 이 질문에는 52 개의 공감대가 있으며 누군가 주제를 "비 주제"로 마무리하기로 결정했습니다.
stackoverflowuser2010

1
그래, 나는 전에이 문제에 대해 Meta에 게시했지만 꽤 심하게 구타를 당했다.
Chris Dutrow

8
이 질문은 다시 열어야합니다. 짧은 코드 솔루션으로 설명 된 문제 (암호 인증)를 해결하기 위해 프로그램을 작성하는 방법에 대한 질문입니다. 방아쇠 단어 "라이브러리"를 본다고해서 반복적으로 질문을 끝내는 것은 아닙니다. 그는 도서관 추천을 요구하지 않고 암호를 해시하는 방법을 요구하고 있습니다. 편집 : 거기에 고정했습니다.
erickson

답변:


157

실제로 Java 런타임에 내장 된 기능을 사용하여이를 수행 할 수 있습니다. Java 6의 SunJCE는 비밀번호 해싱에 사용하기에 적합한 알고리즘 인 PBKDF2를 지원합니다.

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;

/**
 * Hash passwords for storage, and test passwords against password tokens.
 * 
 * Instances of this class can be used concurrently by multiple threads.
 *  
 * @author erickson
 * @see <a href="http://stackoverflow.com/a/2861125/3474">StackOverflow</a>
 */
public final class PasswordAuthentication
{

  /**
   * Each token produced by this class uses this identifier as a prefix.
   */
  public static final String ID = "$31$";

  /**
   * The minimum recommended cost, used by default
   */
  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("\\$31\\$(\\d\\d?)\\$(.{43})");

  private final SecureRandom random;

  private final int cost;

  public PasswordAuthentication()
  {
    this(DEFAULT_COST);
  }

  /**
   * Create a password manager with a 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 a password in an immutable {@code String}. 
   * 
   * <p>Passwords should be stored in a {@code char[]} so that it can be filled 
   * with zeros after use instead of lingering on the heap and elsewhere.
   * 
   * @deprecated Use {@link #hash(char[])} instead
   */
  @Deprecated
  public String hash(String password)
  {
    return hash(password.toCharArray());
  }

  /**
   * Authenticate with a password in an immutable {@code String} and a stored 
   * password token. 
   * 
   * @deprecated Use {@link #authenticate(char[],String)} instead.
   * @see #hash(String)
   */
  @Deprecated
  public boolean authenticate(String password, String token)
  {
    return authenticate(password.toCharArray(), token);
  }

}

11
다음과 같이 바이트 대 16 진수 변환에 대해 조심해야 할 수도 있습니다 BigInteger. 그것은 빠른 디버그에는 괜찮지 만 그 효과로 인해 프로덕션 코드에서 버그를 보았습니다.
Thomas Pornin

24
@ thomas-pornin의 하이라이트는 거의 코드 블록이 아닌 라이브러리 가 필요한 이유를 강조 합니다. 받아 들여진 대답이 그러한 중요한 주제에 관한 질문에 대답하지 않는다는 것은 무섭습니다.
Nilzor

9
Java 8부터 시작하는 알고리즘 PBKDF2WithHmacSHA512를 사용하십시오. 조금 강력합니다.
iwan.z

1
참고 : 기존 alg는 이후 버전에서 삭제되지 않습니다. java_4 : PBEWithMD5AndDES, DESede, DES java_5 / 6 / 7 : PBKDF2WithHmacSHA1, PBE (Java 5에만 해당), PBEWithSHA1AndRC2_40, PBEWithSHA1And, PBEWithMD1, PbeWithMD5, Triple java_8 : PBE WithH , PBEWithHmacSHA1AndAES_128, RC4_128, PBKDF2WithHmacSHA224, PBEWithHmacSHA256AndAES_256, RC2_128, PBEWithHmacSHA224AndAES_256, PBEWithHmacSHA384AndAES_256, PBEWithHmacSHA512AndAES_256, PBKDF2WithHmacSHA512, PBEWithHmacSHA256AndAES_128, PBKDF2WithHmacSHA384, PBEWithHmacSHA1AndAES_256
iwan.z

4
@TheTosters 그렇습니다 . 틀린 암호의 경우 실행 시간이 길어집니다 . 보다 구체적으로, 잘못된 암호는 올바른 암호와 같은 시간이 걸립니다. 이 경우 취약점을 악용 할 수있는 실질적인 방법을 생각할 수 없다고 고백하지만 타이밍 공격을 방지 합니다. 그러나 당신은 모서리를 자르지 않습니다. 내가 그것을 볼 수 없다고해서 더 악의적 인 생각이 아니라는 것을 의미하지는 않습니다.
erickson

97

다음은 원하는 것을 정확하게 수행하는 두 가지 방법 으로 완벽한 구현 입니다.

String getSaltedHash(String password)
boolean checkPassword(String password, String stored)

요점은 공격자가 데이터베이스와 소스 코드에 모두 액세스하더라도 암호는 여전히 안전하다는 것입니다.

import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.security.SecureRandom;
import org.apache.commons.codec.binary.Base64;

public class Password {
    // The higher the number of iterations the more 
    // expensive computing the hash is for us and
    // also for an attacker.
    private static final int iterations = 20*1000;
    private static final int saltLen = 32;
    private static final int desiredKeyLen = 256;

    /** Computes a salted PBKDF2 hash of given plaintext password
        suitable for storing in a database. 
        Empty passwords are not supported. */
    public static String getSaltedHash(String password) throws Exception {
        byte[] salt = SecureRandom.getInstance("SHA1PRNG").generateSeed(saltLen);
        // store the salt with the password
        return Base64.encodeBase64String(salt) + "$" + hash(password, salt);
    }

    /** Checks whether given plaintext password corresponds 
        to a stored salted hash of the password. */
    public static boolean check(String password, String stored) throws Exception{
        String[] saltAndHash = stored.split("\\$");
        if (saltAndHash.length != 2) {
            throw new IllegalStateException(
                "The stored password must have the form 'salt$hash'");
        }
        String hashOfInput = hash(password, Base64.decodeBase64(saltAndHash[0]));
        return hashOfInput.equals(saltAndHash[1]);
    }

    // using PBKDF2 from Sun, an alternative is https://github.com/wg/scrypt
    // cf. http://www.unlimitednovelty.com/2012/03/dont-use-bcrypt.html
    private static String hash(String password, byte[] salt) throws Exception {
        if (password == null || password.length() == 0)
            throw new IllegalArgumentException("Empty passwords are not supported.");
        SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
        SecretKey key = f.generateSecret(new PBEKeySpec(
            password.toCharArray(), salt, iterations, desiredKeyLen));
        return Base64.encodeBase64String(key.getEncoded());
    }
}

우리는 저장하고 'salt$iterated_hash(password, salt)'있습니다. 소금은 32 개의 임의 바이트이며 목적은 두 사람이 같은 암호를 선택하면 저장된 암호가 여전히 다르게 보일 것입니다.

iterated_hash기본적으로 이는, hash(hash(hash(... hash(password, salt) ...)))추측 암호 해시 그들에게 데이터베이스에 액세스 할 수있는 잠재적 인 공격자가 매우 비싼 만들고, 데이터베이스에 해시를 찾아보십시오. iterated_hash사용자가 로그인 할 때마다 이를 계산해야 하지만 해시 계산에 거의 100 %를 소비하는 공격자와 비교할 때 비용이 많이 들지 않습니다.


14
잔소리로 죄송하지만 기존 라이브러리보다 왜 이것을 선택해야하나요? 라이브러리는 아마도 철저하게 검토 될 가능성이 더 높습니다. 14 가지의 투표권이 문제에 대해 코드를 분석했다고 의심합니다.
Joachim Sauer

2
@JoachimSauer 이것은 본질적으로 라이브러리 (javax.crypto)를 사용하고 있지만 옳습니다-빈 암호는 지원되지 않습니다. 명시 적으로 만들기 위해 예외를 추가했습니다. 감사!
Martin Konicek

3
메소드 서명을 char[] password대신로 변경해야합니다 String password.
assylias

2
비록 참조가 만장일치의 동의를 얻지 못하는 것 같습니다. 참조 : security.stackexchange.com/a/20369/12614
assylias

3
문자열의 .equals ()가 단락되지 않습니까? 암호 해시에 대한 정보가 유출 될 경우 타이밍 공격의 위험이 있습니다.
bobpoekert


7

OWASP에서 설명 하는 Shiro 라이브러리 (이전의 JSecurity ) 구현 을 사용할 수 있습니다 .

또한 JASYPT 라이브러리에도 비슷한 유틸리티 가있는 것 같습니다 .


그게 실제로 내가 사용한 것입니다. 그러나 시로를 사용하지 않기로 결정했기 때문에 시로 라이브러리 전체를 하나의 패키지에 포함시켜야하는 비 효율성에 대한 우려가있었습니다.
Chris Dutrow

암호 해싱 유틸리티로 구성된 라이브러리를 모르겠습니다. 종속성이 문제가 될 경우 자신의 롤링을 해제하는 것이 좋습니다. 에릭슨의 대답은 나에게 꽤 좋아 보인다. 또는 안전한 방식으로 SHA를 사용하려는 경우 참조한 OWASP 링크에서 코드를 복사하십시오.
laz

7

을 사용하여 해시를 계산할 수 MessageDigest있지만 보안 측면에서는 잘못되었습니다. 해시는 암호를 쉽게 해독 할 수 있으므로 암호를 저장하는 데 사용해서는 안됩니다.

bcrypt, PBKDF2 및 scrypt와 같은 다른 알고리즘을 사용하여 비밀번호를 저장해야합니다. 여기를 참조하십시오 .


3
데이터베이스에 소금을 저장하지 않고 로그인 할 때 비밀번호를 어떻게 해시 하시겠습니까?
ZZ 코더

9
사용자 이름을 소금으로 사용하는 것은 치명적인 결함이 아니지만 암호화 RNG의 소금을 사용하는 것만 큼 좋은 곳은 없습니다. 그리고 데이터베이스에 소금을 저장하는 데 아무런 문제가 없습니다. 소금은 비밀이 아닙니다.
erickson

1
사용자 이름과 이메일도 데이터베이스에 저장되지 않습니까?
Chris Dutrow

@ZZ Coder, @erickson 맞습니다. 어쨌든 모든 암호에 대해 하나의 소금이 될 것이라고 가정했으며, 이는 쉽게 계산 가능한 무지개 테이블로 이어질 것입니다.
Bozho

13
사용자 이름 (또는 이메일과 같은 다른 ID)을 솔트로 사용하는 데있어 한 가지 문제점은 사용자가 새 비밀번호를 설정하지 않아도 ID를 변경할 수 없다는 것입니다.
Lawrence Dol

6

또한 bcrypt하고 PBKDF2는 다른 답변에 언급, 내가보고 추천 할 scrypt

MD5 및 SHA-1은 상대적으로 빠르므로 "시간당 대여"분산 컴퓨팅 (예 : EC2)을 사용하거나 최신 고급 GPU를 사용하면 비교적 저렴한 비용으로 합리적인 무차별 강제 / 사전 공격을 사용하여 암호를 "크랙"할 수 있으므로 권장하지 않습니다. 시각.

이를 사용해야하는 경우 최소한 미리 정의 된 상당한 횟수 (1000+)의 알고리즘을 반복하십시오.


6

PBKDF2 가 정답 이라는 Erickson의 의견에 전적으로 동의합니다 .

해당 옵션이 없거나 해시 만 사용해야하는 경우 Apache Commons DigestUtils가 JCE 코드를 올바르게 얻는 것보다 훨씬 쉽습니다 : https://commons.apache.org/proper/commons-codec/apidocs/org/apache /commons/codec/digest/DigestUtils.html

해시를 사용하는 경우 sha256 또는 sha512와 함께 사용하십시오. 이 페이지에는 암호 처리 및 해싱에 대한 권장 사항이 있습니다 (암호 처리에는 해싱을 권장하지 않습니다). http://www.daemonology.net/blog/2009-06-11-cryptographic-right-answers.html


SHA512가 숫자가 더 커서 SHA256 (이 목적을 위해)보다 낫다는 점에 주목할 가치가 있습니다.
Azsgy

5

PBKDF2 , BCrypt , SCryptArgon2 비밀번호 암호화 를 지원 하는 Spring Security Crypto ( 2 개의 선택적 컴파일 종속성 만 있음)를 사용할 수 있습니다 .

Argon2PasswordEncoder argon2PasswordEncoder = new Argon2PasswordEncoder();
String aCryptedPassword = argon2PasswordEncoder.encode("password");
boolean passwordIsValid = argon2PasswordEncoder.matches("password", aCryptedPassword);
SCryptPasswordEncoder sCryptPasswordEncoder = new SCryptPasswordEncoder();
String sCryptedPassword = sCryptPasswordEncoder.encode("password");
boolean passwordIsValid = sCryptPasswordEncoder.matches("password", sCryptedPassword);
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
String bCryptedPassword = bCryptPasswordEncoder.encode("password");
boolean passwordIsValid = bCryptPasswordEncoder.matches("password", bCryptedPassword);
Pbkdf2PasswordEncoder pbkdf2PasswordEncoder = new Pbkdf2PasswordEncoder();
String pbkdf2CryptedPassword = pbkdf2PasswordEncoder.encode("password");
boolean passwordIsValid = pbkdf2PasswordEncoder.matches("password", pbkdf2CryptedPassword);

4

그동안 NIST 추천 PBKDF2가 이미 언급 된, 나는 공공 있다는 것을 지적하고 싶습니다 암호 해시 대회 2013 결국 2015 년에 달렸다는 것을 Argon2가 권장되는 암호 해시 함수로 선택되었다가.

사용할 수있는 원래 (기본 C) 라이브러리에 대해 상당히 잘 채택 된 Java 바인딩 이 있습니다.

평균 사용 사례에서 Argon2 대신 PBKDF2를 선택하거나 그 반대의 경우 보안 관점에서 문제가되지 않는다고 생각합니다. 강력한 보안 요구 사항이있는 경우 평가에서 Argon2를 고려하는 것이 좋습니다.

암호 해시 기능의 보안에 대한 자세한 내용은 다음을 참조 security.se을 .


@ zaph 더 객관적으로 답변을 편집했습니다. NIST 권장 사항이 항상 최선의 선택 일 수는 없습니다 ( 예를 들어 여기 참조 ). 물론 다른 곳에서도 권장되는 사항에는 해당됩니다. 따라서이 답변 이이 질문에 가치를 제공한다고 생각합니다.
Qw3ry

2

MD5 해싱 및 기타 해시 방법에 대한 두 개의 링크가 있습니다.

Javadoc API : https://docs.oracle.com/javase/1.5.0/docs/api/java/security/MessageDigest.html

튜토리얼 : http://www.twmacinta.com/myjava/fast_md5.php


3
비밀번호 해싱의 경우 속도가 느릴수록 좋습니다. "키 강화"기술로 수천 번의 해시 함수 반복을 사용해야합니다. 또한 소금은 필수적입니다.
erickson

바이트 길이가 여전히 동일하기 때문에 품질 해싱 알고리즘의 여러 반복이 한 번의 반복과 거의 동일한 보안을 생성한다는 인상을 받았습니다.
Chris Dutrow

@erickson 공격자를 명시 적으로 느리게하는 것이 좋습니다.
deamon

6
키 강화 정보 : 사전 계산 된 해시를 사용할 수 없도록 염이 존재합니다. 그러나 공격자는 사전 계산할 필요가 없습니다. 공격자는 올바른 문자열을 찾을 때까지 문자열과 소금을 "즉시"해시 할 수 있습니다. 그러나 해시를 수천 번 반복하면 동일한 작업을 수행해야합니다. 서버는 자주 발생하지 않으므로 10k 반복으로 많은 영향을받지 않습니다. 공격자는 컴퓨팅 성능의 10k 배가 필요합니다.
zockman

2
@Simon의 오늘 MD5는 GPU 무차별 대입 / 사전 공격을 사용하여 몇 초 만에 해독 할 수 있으므로 암호 해싱에 쓸모없는 것으로 간주됩니다. 여기를 참조하십시오 : codahale.com/how-to-safely-store-a-password
에 란 메단

1

모든 표준 해시 체계 중에서 LDAP ssha가 가장 안전합니다.

http://www.openldap.org/faq/data/cache/347.html

여기에 지정된 알고리즘을 따르고 MessageDigest를 사용하여 해시를 수행합니다.

제안한대로 소금을 데이터베이스에 저장해야합니다.


1
SSHA는 해시 함수를 반복하지 않기 때문에 너무 빠릅니다. 이를 통해 공격자는보다 신속하게 암호를 시도 할 수 있습니다. Bcrypt, PBBKDF1 및 PBKDF2와 같은 더 나은 알고리즘은 "키 강화"기술을 사용하여 공격자가 8 자리 암호 공간이라도 무력을 발휘하기 전에 암호가 만료되는 시점까지 속도를 늦 춥니 다.
erickson

이러한 모든 메커니즘의 문제점은 클라이언트 지원을받지 못한다는 것입니다. 해시 된 비밀번호의 문제점은 다른 알고리즘으로 해시 된 비밀번호를 지원할 수 없다는 것입니다. ssha를 사용하면 최소한 모든 LDAP 클라이언트가이를 지원합니다.
ZZ 코더

"가장 안전"하지는 않지만 "꽤 호환 가능"합니다. bcrypt / scrypt는 리소스를 더 집중적으로 강화합니다.
eckes

1

가장 신뢰할 수 있고 유연한 알고리즘 인 2020 년부터

하드웨어에 따라 강도를 최적화 할 가능성이 가장 높은

이다 Argon2id 또는 Argon2i .

대상 해싱 시간과 사용 된 하드웨어에 대해 최적화 된 강도 매개 변수를 찾는 데 필요한 보정 도구를 제공합니다.

  • Argon2i 는 메모리 욕심 해시 전문 기업입니다
  • Argon2d 는 CPU 욕심 많은 해싱에 특화되어 있습니다
  • Argon2id 는 두 가지 방법을 모두 사용합니다.

메모리 욕심 해싱은 크래킹에 GPU 사용을 방지하는 데 도움이됩니다.

Spring security / Bouncy Castle 구현은 공격자가 사용할 수있는 것을 감안할 때 최적화되지 않았으며 상대적으로 일주일에 이루어집니다. cf : 스프링 문서

현재 구현에서는 암호 크래커가 수행 할 병렬 처리 / 최적화를 활용하지 않는 탄력성을 사용하므로 공격자와 방어자 사이에 불필요한 비대칭 성이 있습니다.

자바에 사용되는 가장 신뢰할만한 구현은 mkammerer입니다 .

Rust로 작성된 공식 네이티브 구현의 래퍼 jar / 라이브러리

잘 작성되어 있고 사용하기 쉽습니다.

임베디드 버전은 Linux, Windows 및 OSX를위한 기본 빌드를 제공합니다.

예를 들어, jpmorganchase 는 Ethereum cryptocurency 구현 인 Quorum 을 보호하는 데 사용되는 테 세라 보안 프로젝트 에서 jpmorganchase에 의해 사용됩니다 .

다음 은 tessera의 예제 코드입니다.

de.mkammerer.argon2.Argon2Helper # findIterations를 사용하여 보정을 수행 할 수 있습니다.

SCRYPT 및 Pbkdf2 알고리즘은 간단한 벤치 마크를 작성하여 교정 할 수도 있지만 현재 최소 안전 반복 값은 더 높은 해싱 시간이 필요합니다.


0

나는 udemy의 비디오에서 배웠고 더 강력한 임의 암호로 편집되었습니다.

}

private String pass() {
        String passswet="1234567890zxcvbbnmasdfghjklop[iuytrtewq@#$%^&*" ;

        char icon1;
        char[] t=new char[20];

         int rand1=(int)(Math.random()*6)+38;//to make a random within the range of special characters

            icon1=passswet.charAt(rand1);//will produce char with a special character

        int i=0;
        while( i <11) {

             int rand=(int)(Math.random()*passswet.length());
             //notice (int) as the original value of Math>random() is double

             t[i] =passswet.charAt(rand);

             i++;
                t[10]=icon1;
//to replace the specified item with icon1
         }
        return new String(t);
}






}

나는 수정되고 있지만 해싱 할 때 임의의 숫자를 사용해서는 안된다고 생각합니다. 이것은 해시 함수가 결정 론적으로 유지되도록하기위한 것입니다. 즉, 문자열을 여러 번 해시하면 해당 문자열에 대해 항상 동일한 해시 값을 다시 얻게됩니다.
duldi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.