Question

Je dois les mots de passe de hachage pour le stockage dans une base de données. Comment puis-je faire cela en Java?

J'espérais prendre le mot de passe de texte brut, ajoutez un sel aléatoire, puis stocker le sel et le mot de passe haché dans la base de données.

Ensuite, lorsqu'un utilisateur souhaite se connecter, je pouvais prendre leur mot de passe soumis, ajouter le sel au hasard de leurs informations de compte, hachage et voir si elle correspond au mot de passe de hachage stockée avec leurs informations de compte.

Était-ce utile?

La solution

Vous pouvez réellement utiliser une installation construite pour le moteur d'exécution Java pour le faire. Le SunJCE en Java 6 supports PBKDF2, ce qui est un bon algorithme à utiliser pour le hachage de mot de passe.

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

Voici une classe utilitaire que vous pouvez utiliser pour l'authentification par mot de passe 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);
  }

}

Autres conseils

Voici un mise en œuvre complète avec deux méthodes de faire exactement ce que vous voulez:

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

Le point est que même si un attaquant obtient un accès à la fois la base de données et le code source, les mots de passe sont toujours en sécurité.

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

Nous stockons 'salt$iterated_hash(password, salt)'. Le sel sont 32 octets aléatoires et son but est que si deux personnes différentes choisissent le même mot de passe, les mots de passe stockés sera toujours différent.

Le iterated_hash, qui est essentiellement hash(hash(hash(... hash(password, salt) ...))) fait très cher pour un attaquant potentiel qui a accès à votre base de données de deviner les mots de passe, hachage eux, et rechercher hash dans la base de données. Vous devez calculer ce iterated_hash chaque fois qu'un utilisateur ouvre une session dans, mais il ne vous coûte pas beaucoup par rapport à l'attaquant qui passe près de 100% de leur temps de calcul hash.

Bcrypt est une très bonne bibliothèque, et il y a un port Java de celui-ci.

Vous pouvez comput hash en utilisant MessageDigest , mais cela est faux en termes de sécurité. Hash ne doivent pas être utilisés pour le stockage des mots de passe, car ils sont facilement cassable.

Vous devez utiliser un autre algorithme comme bcrypt, PBKDF2 et scrypt pour stocker les mots de passe que vous. Voir .

Vous pouvez utiliser de la bibliothèque Shiro (anciennement < a href = "http://www.jsecurity.org/" rel = "noreferrer" title = "JSecurity"> JSecurity ) la mise en œuvre de ce qui est décrit par OWASP .

Il ressemble également à la bibliothèque de JASYPT a utilitaire.

En plus de bcrypt et PBKDF2 mentionné dans d'autres réponses, je vous recommande de regarder scrypt

MD5 et SHA-1 ne sont pas recommandées car elles utilisent relativement rapide donc « louer par heure » calcul distribué (par exemple EC2) ou un GPU haut de gamme moderne peut les mots de passe « crack » en utilisant la force brute / attaques par dictionnaire dans relativement faible les coûts et les délais raisonnables.

Si vous devez les utiliser, au moins itérer l'algorithme d'une quantité importante de fois prédéfini (1000 +).

Entièrement d'accord avec Erickson que PBKDF2 est la réponse.

Si vous ne possédez pas cette option, ou seulement besoin d'utiliser un hachage, Apache Commons DigestUtils est beaucoup plus facile que d'obtenir le droit de code JCE: https: //commons.apache .org / commons-codec / / apidocs / org / apache / communes / codec / digest / DigestUtils.html

Si vous utilisez un hachage, rendez-vous avec SHA256 ou SHA512. Cette page a de bonnes recommandations sur la gestion des mots de passe et hash (note qu'il ne recommande pas de hachage pour la gestion des mots de passe): http://www.daemonology.net/blog/2009 -cryptographique -06 à 11 droite answers.html

Alors que la recommandation NIST PBKDF2 a déjà été mentionné, je voudrais souligner qu'il y avait un partenariat public < a href = "https://password-hashing.net/" rel = "nofollow noreferrer"> mot de passe de la concurrence hash qui ont fonctionné de 2013 à 2015. en fin de compte, Argon2 a été choisi comme la fonction de hachage de mot de passe recommandé.

Il y a un assez bien adopté Java de liaison pour la bibliothèque d'origine (C natif) que vous pouvez utiliser.

Dans le cas d'utilisation moyenne, je ne pense pas que ce soit la matière du point de vue de la sécurité si vous choisissez PBKDF2 sur Argon2 ou vice-versa. Si vous avez de fortes exigences en matière de sécurité, je vous recommande l'examen Argon2 dans votre évaluation.

Pour plus d'informations sur la sécurité des mots de passe des fonctions de hachage voir security.se .

Ici vous avez deux liens pour hachage MD5 et d'autres méthodes de hachage:

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

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

Parmi tous les schémas de hachage standard ASIS LDAP est le plus à une utilisation sécurisée,

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

Je voudrais juste suivre les algorithmes spécifiés là-bas et utiliser MessageDigest pour faire le hachage.

Vous devez stocker comme vous le suggérez le sel dans votre base de données.

Je l'ai appris à partir d'une vidéo sur Udemy et modifié pour être fort mot de passe aléatoire

}

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






}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top