Frage

muss ich Hash-Passwörter für die Speicherung in einer Datenbank. Wie kann ich dies tun in Java?

Ich habe gehofft, das Klartext-Passwort zu nehmen, fügen Sie ein zufälliges Salz, dann speichern Sie das Salz und das Hash-Passwort in der Datenbank.

Dann, wenn ein Benutzer anmelden wollte, könnte ich ihr vorgelegtes Passwort nehmen, fügen Sie das zufällige Salz aus ihren Kontoinformationen, Hash es und sehen, ob sie mit ihren Kontodaten mit dem gespeicherten Hash-Passwort entsprechen.

War es hilfreich?

Lösung

Sie können tatsächlich eine Anlage in Runtime Java gebaut, dies zu tun. Die SunJCE in Java 6 unterstützt PBKDF2, die für Passwort-Hashing ein guter Algorithmus verwendet wird.

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

Hier ist eine Utility-Klasse, die Sie für PBKDF2 Passwort-Authentifizierung verwenden können:

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

}

Andere Tipps

Hier ist eine vollständige Implementierung mit zwei Methoden genau das tun, was Sie wollen:

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

Der Punkt ist, dass selbst wenn ein Angreifer Zugriff auf beiden Codes Ihrer Datenbank und Quelle bekommt, die Passwörter noch sicher sind.

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

Wir speichern 'salt$iterated_hash(password, salt)'. Das Salz ist 32 zufällige Bytes und es Absicht ist, dass, wenn zwei verschiedene Personen das gleiche Passwort wählen, nach wie vor die gespeicherten Passwörter anders aussehen werden.

Die iterated_hash, die im Grunde hash(hash(hash(... hash(password, salt) ...))) ist, macht es sehr teuer für einen potentiellen Angreifer, der Zugriff auf die Datenbank hat, Kennwörter zu erraten, Hash sie und Hashes in der Datenbank nachschlagen. Sie haben dieses iterated_hash sich ein Benutzer anmeldet, jedes Mal zu berechnen, aber es macht Sie nicht kosten so viel im Vergleich zu dem Angreifer, der fast 100% ihrer Zeit Berechnung Hashes verbringt.

ist BCrypt eine sehr gute Bibliothek, und es gibt einen Java-Port davon.

Sie können die Hashes Comput mit MessageDigest , aber das ist falsch in Bezug auf die Sicherheit. Hashes ist nicht zum Speichern von Passwörtern verwendet werden, da sie leicht zerbrechlich sind.

Sie sollten einen anderen Algorithmus wie bcrypt, PBKDF2 verwenden und Scrypt speichern Sie Passwörter. Siehe hier .

Sie können mit der Shiro Bibliothek (früher < a href = "http://www.jsecurity.org/" rel = "noreferrer" title = "JSecurity"> JSecurity ) Implementierung von dem, was von OWASP .

Es sieht auch wie die JASYPT Bibliothek ein ähnlicher Dienstprogramm .

Neben bcrypt und PBKDF2 in anderen Antworten erwähnt, würde ich empfehlen, sich unter Scrypt

MD5 und SHA-1 sind nicht zu empfehlen, da sie relativ schnell so mit „mieten pro Stunde“ distributed computing (zB EC2) oder ein modernes High-End-GPU sind eins kann „knacken“ Passwörter mit Brute-Force / Wörterbuch-Attacken in relativ niedrigen Kosten und angemessene Zeit.

Wenn Sie müssen sie verwenden, dann zumindest Iterierte der Algorithmus eine vordefinierte erhebliche Menge an Zeit (1000 +).

voll zustimmen Erickson, dass PBKDF2 ist die Antwort.

Wenn Sie diese Option nicht haben, oder brauchen nur einen Hash zu verwenden, Apache Commons DigestUtils ist viel einfacher als JCE Code richtig hinzubekommen: https: //commons.apache .org / die richtige / commons-codec / apidocs / org / apache / commons / codec / Digest / DigestUtils.html

Wenn Sie einen Hash verwenden, gehen Sie mit sha256 oder sha512. Diese Seite hat gute Empfehlungen für Umgang mit Passwörtern und Hashing (Anmerkung nicht Hashing für Passwort Handhabung nicht empfehlen): http://www.daemonology.net/blog/2009 -06-11-Verschlüsselungs-rechts-answers.html

Während die NIST Empfehlung PBKDF2 wird bereits erwähnt, würde Ich mag darauf hinweisen, dass es ein öffentliches war < a href = "https://password-hashing.net/" rel = "nofollow noreferrer"> Kennwort-Hashing-Wettbewerb dieser läuft von 2013 bis 2015. Am Ende Argon2 als die empfohlene Kennwort-Hashing-Funktion gewählt wurde.

Es gibt ein recht gut angenommen Java Bindung für die ursprüngliche (native C) Bibliothek, dass Sie können verwendet werden.

Im Durchschnitt Use-Case, ich glaube nicht, es ist nicht egal aus Sicht der Sicherheit, wenn Sie PBKDF2 über Argon2 oder umgekehrt wählen. Wenn Sie starke Sicherheitsanforderungen haben, empfehle ich Argon2 in Ihrer Bewertung berücksichtigen.

Für weitere Informationen über die Sicherheit der Passwort-Hashing-Funktionen finden Sie unter security.se .

Hier haben Sie zwei Links für MD5-Hashing und andere Hash-Methoden:

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

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

könnten Sie Spring Security Crypto (nur 2 optional Kompilierung Abhängigkeiten ), die unterstützt 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 und SCryp t Passwort-Verschlüsselung.

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

Unter allen Standard-Hash-Systeme, LDAP ssha ist die sicherste ein Gebrauch,

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

Ich würde folgen Sie einfach die Algorithmen dort angegebenen und verwenden Message den Hash zu tun.

Sie müssen das Salz in der Datenbank speichern, wie Sie vorgeschlagen.

i gelernt, dass von einem Video auf Udemy und bearbeiten starke zufällige Passwort sein

}

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






}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top