Pregunta

necesito contraseñas de hash para su almacenamiento en una base de datos. ¿Cómo puedo hacer esto en Java?

Yo tenía la esperanza de tomar la contraseña en texto plano, añadir una sal al azar, a continuación, almacenar la sal y la contraseña con algoritmo hash en la base de datos.

A continuación, cuando un usuario desea conectarse, podría tomar su contraseña presentado, añadir la sal al azar de información de sus cuentas, hachís y ver si se compara con el hash de la contraseña almacenada con su información de cuenta.

¿Fue útil?

Solución

En realidad se puede utilizar una instalación integrada en el tiempo de ejecución de Java para hacer esto. El SunJCE en Java 6 soportes PBKDF2, que es un algoritmo bueno utilizar para hash de la clave.

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

Esta es una clase de utilidad que se puede utilizar para la autenticación de contraseña 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);
  }

}

Otros consejos

Aquí es un aplicación completa con dos métodos de hacer exactamente lo que quiere:

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

El punto es que, incluso si un atacante obtiene acceso tanto a su base de datos y código fuente, las contraseñas están a salvo.

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

Estamos almacenando 'salt$iterated_hash(password, salt)'. La sal son 32 bytes aleatorios y su propósito es que si dos personas diferentes eligen la misma contraseña, las contraseñas almacenadas todavía tendrán un aspecto diferente.

El iterated_hash, que es básicamente hash(hash(hash(... hash(password, salt) ...))) hace que sea muy caro para un atacante potencial que tiene acceso a la base de datos de adivinar contraseñas, hachís, y mirar hacia arriba hash en la base de datos. Usted tiene que calcular esta iterated_hash cada vez que un usuario se conecta, pero no le cuesta mucho en comparación con el atacante que gasta casi el 100% de su tiempo de computación hashes.

Bcrypt es una muy buena biblioteca, y hay una Java puerto de la misma.

Puede Comput hash utilizando MessageDigest , pero esto está mal en términos de seguridad. Hashes no deben ser utilizados para el almacenamiento de contraseñas, ya que son fácilmente rompible.

Se debe utilizar otro algoritmo como bcrypt, PBKDF2 y scrypt para almacenar las contraseñas que usted. Vea aquí .

Puede utilizar el Shiro de (anteriormente la biblioteca < a href = "http://www.jsecurity.org/" rel = "noreferrer" title = "JSecurity"> JSecurity ) aplicación de lo que está descrito por OWASP .

También se parece a la biblioteca Jasypt tiene un utilidad similar .

Además de bcrypt y PBKDF2 mencionado en otras respuestas, yo recomiendo mirar scrypt

MD5 y SHA-1 no son recomendables, ya que son relativamente rápido por lo tanto el uso de "alquilar por hora" Distributed Computing (por ejemplo EC2) o una moderna gama alta GPU se puede "piratear" las contraseñas usando la fuerza bruta / ataques de diccionario en relativamente baja costes y tiempo razonable.

Si tiene que usarlos, entonces por lo menos iterar el algoritmo de una cantidad significativa de veces predefinido (1000 +).

totalmente de acuerdo con Erickson que PBKDF2 es la respuesta.

Si usted no tiene esa opción, o sólo tendrá que utilizar un hash, Apache Commons DigestUtils es mucho más fácil que conseguir el código JCE derecha: https: //commons.apache .org / / commons-codec adecuados / apidocs / org / apache / Commons / codec / digerir / DigestUtils.html

Si utiliza un hash, ir con SHA256 o SHA512. Esta página no tiene buenas recomendaciones sobre el manejo de contraseñas y hash (nota que no recomienda hash para el manejo de contraseñas): http://www.daemonology.net/blog/2009 -06-11-criptográfica de derecha answers.html

Mientras que el NIST recomendación PBKDF2 ya se ha mencionado, me gustaría señalar que había un público < a href = "https://password-hashing.net/" rel = "nofollow noreferrer"> contraseña de la competencia de hash que funcionó desde 2013 hasta 2015. al final, ARGON2 fue elegida como la función de hash de contraseñas recomendada.

Hay una unión de Java bastante bien adoptado para el (nativo C) de la biblioteca original que que puede utilizar.

En el caso de uso normal, no creo que sí importa desde una perspectiva de seguridad si decide PBKDF2 sobre ARGON2 o viceversa. Si usted tiene requisitos de seguridad fuertes, recomiendo considerar ARGON2 en su evaluación.

Para más información sobre la seguridad de las funciones hash de la clave ver security.se .

Aquí tienes dos enlaces para hash MD5 y otros métodos de hash:

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

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

Spring Security Crypto (sólo tiene 2 opcional de compilación dependencias ), que apoya 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 y SCryp t cifrado de la contraseña.

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

Entre todos los esquemas de hash estándar, SSHA LDAP es el más seguro de usar,

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

Me gustaría simplemente seguir los algoritmos especificados allí y utilizar MessageDigest hacer el hash.

Es necesario para almacenar la sal en su base de datos como usted sugiere.

Eso lo aprendí de un video en Udemy y editado para ser fuerte contraseña aleatoria

}

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






}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top