Domanda

ho bisogno di password hash per la memorizzazione in un database. Come posso fare questo in Java?

speravo di prendere la password in formato testo, aggiungere un sale casuale, quindi memorizzare il sale e l'hash della password nel database.

Poi, quando un utente vuole accedere, ho potuto prendere il loro parola d'ordine presentata, aggiungere il sale a caso da loro informazioni di account, hash e vedere se corrisponde alla password hash memorizzato con le loro informazioni di account.

È stato utile?

Soluzione

Si può effettivamente utilizzare una struttura costruita per il runtime Java per fare questo. Il SunJCE in Java 6 supporti PBKDF2, che è un buon algoritmo da utilizzare per l'hashing delle password.

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

Ecco una classe di utilità che è possibile utilizzare per l'autenticazione della password 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);
  }

}

Altri suggerimenti

Ecco un completa realizzazione con due metodi di fare esattamente quello che vuoi:

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

Il punto è che, anche se un utente malintenzionato ottiene l'accesso sia al codice del database e la fonte, le password sono ancora al sicuro.

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

abbiamo memorizzato 'salt$iterated_hash(password, salt)'. Il sale sono 32 byte casuali e Il suo scopo è che se due persone differenti scegliere la stessa password, le password memorizzate saranno ancora un aspetto diverso.

Il iterated_hash, che è fondamentalmente hash(hash(hash(... hash(password, salt) ...))) rende molto costoso per un potenziale aggressore che ha accesso al database di indovinare le password, hash loro, e cercare gli hash nel database. Bisogna calcolare questo iterated_hash ogni volta che un utente si collega, ma non costa più di tanto rispetto per l'attaccante che spende quasi il 100% del loro tempo di calcolo hash.

bcrypt è una buona biblioteca, e c'è un Java porto di esso.

È possibile comput hash utilizzando MessageDigest , ma questo è sbagliato in termini di sicurezza. Hash non devono essere utilizzati per memorizzare password, in quanto sono facilmente frangibile.

Si dovrebbe usare un altro algoritmo come bcrypt, PBKDF2 e scrypt per memorizzare le password voi. Vedi qui .

È possibile utilizzare la Shiro di (ex biblioteca < a href = "http://www.jsecurity.org/" rel = "noreferrer" title = "JSecurity"> JSecurity ) implementazione di ciò che viene descritto da OWASP .

Sembra inoltre che la biblioteca JASYPT ha una simile utility .

Oltre a bcrypt e PBKDF2 menzionato in altre risposte, mi raccomando guardando scrypt

MD5 e SHA-1 non sono raccomandati in quanto sono relativamente veloce in tal modo l'utilizzo di "affittare per ora" Distributed Computing (ad esempio EC2) o di un moderno di fascia alta GPU uno can password "crack" che usano la forza bruta / attacchi dizionario a relativamente bassa costi e tempi ragionevoli.

Se li si deve usare, almeno iterare l'algoritmo di una notevole quantità predefinito di volte (1000 +).

Completamente d'accordo con Erickson che PBKDF2 è la risposta.

Se non si dispone di tale opzione, o solo bisogno di utilizzare un hash, Apache Commons DigestUtils è molto più facile che ottenere il codice JCE destra: https: //commons.apache .org / corretto / commons-codec / apidocs / org / apache / comuni / codec / digest / DigestUtils.html

Se si utilizza un hash, andare con sha256 o sha512. Questa pagina ha buoni consigli su gestione password e hashing (nota che non consiglia hashing per la gestione password): http://www.daemonology.net/blog/2009 -06-11-crittografia-destra-answers.html

Mentre il NIST raccomandazione PBKDF2 è già stato menzionato, vorrei far notare che c'era un pubblico < a href = "https://password-hashing.net/" rel = "nofollow noreferrer"> la password hashing concorrenza che va dal 2013 al 2015. alla fine, Argon2 è stato scelto come la funzione di hashing delle password consigliata.

C'è un Java abbastanza bene adottata vincolante per il (nativo C) libreria originale che è possibile utilizzare.

Nel caso d'uso media, io non credo che ha importanza dal punto di vista della sicurezza, se si sceglie PBKDF2 sopra Argon2 o viceversa. Se si dispone di requisiti di sicurezza forti, vi consiglio considerando Argon2 nella vostra valutazione.

Per ulteriori informazioni sulla sicurezza delle funzioni di hashing delle password vedere security.se .

Qui ci sono due link per MD5 hash e altri metodi di hash:

Javadoc API: http: // java.sun.com/j2se/1.4.2/docs/api/java/security/MessageDigest.html

Esercitazione: http://www.twmacinta.com/myjava/fast_md5.php

Si potrebbe usare Primavera di sicurezza Crypto (ha solo 2 opzionale compilazione dipendenze ), che supporta PBKDF2 , < a href = "https://docs.spring.io/spring-security/site/docs/5.1.4.RELEASE/api/org/springframework/security/crypto/bcrypt/package-frame.html" rel = "nofollow noreferrer "> bcrypt e SCryp t crittografia delle password.

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

Tra tutti gli schemi di hash standard, SSHA LDAP è il massimo che si sicuro per l'uso,

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

Vorrei solo seguire gli algoritmi non specificato e utilizzare MessageDigest per fare l'hash.

È necessario memorizzare il sale nel database come lei ha suggerito.

L'ho imparato da un video su Udemy e modificato per essere forte password casuale

}

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






}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top