2013-08-09 27 views
31

Etrafa bakıyorum ve en yakın cevabı: How to generate a random alpha-numeric string?Tuzlu Hash için Java'da nasıl SALT üretirim?

Ben CrackStation tutorial buna göre bu iş akışını takip etmek istiyorum:

  1. üret Bir Parola Mağaza CSPRNG kullanarak uzun bir rasgele tuz.

  2. Prepend şifre tuzu ve SHA256 gibi standart kriptografik hızlı arama fonksiyonu ile karma. tuz ve kullanıcının veritabanı kayıtlarında karma hem kaydet

  3. .

veritabanından kullanıcının tuz ve karma Al bir Parola

  1. doğrulama için.

  2. Prepend

    verilen şifre tuz ve aynı karma işlev kullanarak karma.

  3. veritabanından karma ile verilen şifrenin karmasını karşılaştırın. Eşleşirse şifre doğrudur. Aksi halde şifre yanlış.

Ben bir SALT oluşturmak için nasıl bilmiyorum. MessageDigest kullanarak bir karma nasıl üretileceğini anladım. SecureRandom kullanmayı denedim, ancak nextByte yöntemi bozuk kod oluşturuyor.

Düzeltme: Hangi yanıtı seçeceğimi bilmiyorum, onlar benim için çok karmaşık, jBCrypt kullanmaya karar verdiler; jBCript kullanımı kolaydır, sahnelerin arkasındaki tüm karmaşık şeyleri yapar. bu yüzden topluluğun en iyi cevap için oy kullanmasına izin vereceğim.

+2

Eğer bir şifre şifrelemek için kolay bir yol isterseniz java, [jBcrypt] 'a bakın (http://www.mindrot.org/projects/jBCrypt/) :) – NiziL

+0

@Nizil Teşekkürler! Muhtemelen şimdi kullanacağım şey budur. –

+0

@Louie Çok basit görünse de, onaylı bir Güvenlik Sağlayıcısı kullanmak doğru olan şey olacaktır. –

cevap

9

Haklısınız sen yani rastgele bir sayı ama onun hiçbir şey tuzunu oluşturmak istiyorum nasıl dair bulundu. Bu özel durum için, sisteminizi olası Sözlük saldırılarından koruyacaktır. Şimdi, ikinci problem için yapabileceğiniz şey UTF-8 kodlaması kullanmak yerine Base64 kullanmak isteyebilirsiniz. İşte, bir karma üretmek için bir örnek. Ben, burada this post ve that post den esinlenerek OWASP

+0

SALT'ı doğru şekilde üretiyorum mu? Sadece yazdırdım çünkü geçerli olup olmadığını bilmek istedim. –

+0

Neyse aksi her şifre için kullanılan tuz kaydetmek gerekir, So kodlama yanlış olmasına rağmen şifresini –

+0

kimlik doğrulaması mümkün olmayacaktır, ben hala doğru Şifremle eklenecek ve benim karma oluşturmak için gidiyor varsayabiliriz? –

34

doğrudan karma şifre standart uygulama bazı ek referanstır

public byte[] generateSalt() { 
     SecureRandom random = new SecureRandom(); 
     byte bytes[] = new byte[20]; 
     random.nextBytes(bytes); 
     return bytes; 
    } 

public String bytetoString(byte[] input) { 
     return org.apache.commons.codec.binary.Base64.encodeBase64String(input); 
    } 

public byte[] getHashWithSalt(String input, HashingTechqniue technique, byte[] salt) throws NoSuchAlgorithmException { 
     MessageDigest digest = MessageDigest.getInstance(technique.value); 
     digest.reset(); 
     digest.update(salt); 
     byte[] hashedBytes = digest.digest(stringToByte(input)); 
     return hashedBytes; 
    } 
public byte[] stringToByte(String input) { 
     if (Base64.isBase64(input)) { 
      return Base64.decodeBase64(input); 

     } else { 
      return Base64.encodeBase64(input.getBytes()); 
     } 
    } 

Kendi birini seçebilir sizi kodlayan base64 yapmak için Apache Ortak Codec'lerinin kullanıyorum Karma şifreli şifreler üretmek ve doğrulamak için bu kodu kullanıyorum. Sadece JDK tarafından sağlanan sınıfları kullanır, harici bağımlılık yoktur.

süreçtir:

  • Eğer kullanıcıya onun şifresini sormak getNextSalt
  • ile bir tuz oluşturmak ve tuzlu ve karma şifre oluşturmak için hash yöntemini kullanın. yöntem tuzun
  • ile bir veritabanında olduğu gibi, Tuzu almak, onun şifresini sormak bir kullanıcının kimliğini doğrulamak için kaydetmek ve veritabanından parola karma ve ayrıntıları eşleştiğini kontrol etmek isExpectedPassword yöntemi kullanabilirsiniz bir byte[] döndürür
/** 
* A utility class to hash passwords and check passwords vs hashed values. It uses a combination of hashing and unique 
* salt. The algorithm used is PBKDF2WithHmacSHA1 which, although not the best for hashing password (vs. bcrypt) is 
* still considered robust and <a href="https://security.stackexchange.com/a/6415/12614"> recommended by NIST </a>. 
* The hashed value has 256 bits. 
*/ 
public class Passwords { 

    private static final Random RANDOM = new SecureRandom(); 
    private static final int ITERATIONS = 10000; 
    private static final int KEY_LENGTH = 256; 

    /** 
    * static utility class 
    */ 
    private Passwords() { } 

    /** 
    * Returns a random salt to be used to hash a password. 
    * 
    * @return a 16 bytes random salt 
    */ 
    public static byte[] getNextSalt() { 
    byte[] salt = new byte[16]; 
    RANDOM.nextBytes(salt); 
    return salt; 
    } 

    /** 
    * Returns a salted and hashed password using the provided hash.<br> 
    * Note - side effect: the password is destroyed (the char[] is filled with zeros) 
    * 
    * @param password the password to be hashed 
    * @param salt  a 16 bytes salt, ideally obtained with the getNextSalt method 
    * 
    * @return the hashed password with a pinch of salt 
    */ 
    public static byte[] hash(char[] password, byte[] salt) { 
    PBEKeySpec spec = new PBEKeySpec(password, salt, ITERATIONS, KEY_LENGTH); 
    Arrays.fill(password, Character.MIN_VALUE); 
    try { 
     SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
     return skf.generateSecret(spec).getEncoded(); 
    } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { 
     throw new AssertionError("Error while hashing a password: " + e.getMessage(), e); 
    } finally { 
     spec.clearPassword(); 
    } 
    } 

    /** 
    * Returns true if the given password and salt match the hashed value, false otherwise.<br> 
    * Note - side effect: the password is destroyed (the char[] is filled with zeros) 
    * 
    * @param password  the password to check 
    * @param salt   the salt used to hash the password 
    * @param expectedHash the expected hashed value of the password 
    * 
    * @return true if the given password and salt match the hashed value, false otherwise 
    */ 
    public static boolean isExpectedPassword(char[] password, byte[] salt, byte[] expectedHash) { 
    byte[] pwdHash = hash(password, salt); 
    Arrays.fill(password, Character.MIN_VALUE); 
    if (pwdHash.length != expectedHash.length) return false; 
    for (int i = 0; i < pwdHash.length; i++) { 
     if (pwdHash[i] != expectedHash[i]) return false; 
    } 
    return true; 
    } 

    /** 
    * Generates a random password of a given length, using letters and digits. 
    * 
    * @param length the length of the password 
    * 
    * @return a random password 
    */ 
    public static String generateRandomPassword(int length) { 
    StringBuilder sb = new StringBuilder(length); 
    for (int i = 0; i < length; i++) { 
     int c = RANDOM.nextInt(62); 
     if (c <= 9) { 
     sb.append(String.valueOf(c)); 
     } else if (c < 36) { 
     sb.append((char) ('a' + c - 10)); 
     } else { 
     sb.append((char) ('A' + c - 36)); 
     } 
    } 
    return sb.toString(); 
    } 
} 
+0

Bunu denemek için gidiyorum, ama apache ortak yöntemleri daha iyi veya saf yerli Java daha iyi olup olmadığını belirlemek için benim için zor. Baska öneri? –

+0

Baytları SQL'de bir BLOB olarak mı depolarım? –

+0

@Louie Sanırım kullandığınız veritabanına bağlı ama BLOB uygun görünüyor. Tavsiyeye ihtiyacınız varsa, bu belirli noktada ayrı bir soru sorabilirsiniz. – assylias

3

SHA-3, ben kullanıyorum BouncyCastle kullanarak başka versiyonu:

arayüzü:

public interface IPasswords { 

    /** 
    * Generates a random salt. 
    * 
    * @return a byte array with a 64 byte length salt. 
    */ 
    byte[] getSalt64(); 

    /** 
    * Generates a random salt 
    * 
    * @return a byte array with a 32 byte length salt. 
    */ 
    byte[] getSalt32(); 

    /** 
    * Generates a new salt, minimum must be 32 bytes long, 64 bytes even better. 
    * 
    * @param size the size of the salt 
    * @return a random salt. 
    */ 
    byte[] getSalt(final int size); 

    /** 
    * Generates a new hashed password 
    * 
    * @param password to be hashed 
    * @param salt the randomly generated salt 
    * @return a hashed password 
    */ 
    byte[] hash(final String password, final byte[] salt); 

    /** 
    * Expected password 
    * 
    * @param password to be verified 
    * @param salt the generated salt (coming from database) 
    * @param hash the generated hash (coming from database) 
    * @return true if password matches, false otherwise 
    */ 
    boolean isExpectedPassword(final String password, final byte[] salt, final byte[] hash); 

    /** 
    * Generates a random password 
    * 
    * @param length desired password length 
    * @return a random password 
    */ 
    String generateRandomPassword(final int length); 
} 

uygulaması:

import org.apache.commons.lang3.ArrayUtils; 
import org.apache.commons.lang3.Validate; 
import org.apache.log4j.Logger; 
import org.bouncycastle.jcajce.provider.digest.SHA3; 

import java.io.Serializable; 
import java.io.UnsupportedEncodingException; 
import java.security.SecureRandom; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 
import java.util.Random; 

public final class Passwords implements IPasswords, Serializable { 

    /*serialVersionUID*/ 
    private static final long serialVersionUID = 8036397974428641579L; 
    private static final Logger LOGGER = Logger.getLogger(Passwords.class); 
    private static final Random RANDOM = new SecureRandom(); 
    private static final int DEFAULT_SIZE = 64; 
    private static final char[] symbols; 

    static { 
      final StringBuilder tmp = new StringBuilder(); 
      for (char ch = '0'; ch <= '9'; ++ch) { 
        tmp.append(ch); 
      } 
      for (char ch = 'a'; ch <= 'z'; ++ch) { 
        tmp.append(ch); 
      } 
      symbols = tmp.toString().toCharArray(); 
    } 

    @Override public byte[] getSalt64() { 
      return getSalt(DEFAULT_SIZE); 
    } 

    @Override public byte[] getSalt32() { 
      return getSalt(32); 
    } 

    @Override public byte[] getSalt(int size) { 
      final byte[] salt; 
      if (size < 32) { 
        final String message = String.format("Size < 32, using default of: %d", DEFAULT_SIZE); 
        LOGGER.warn(message); 
        salt = new byte[DEFAULT_SIZE]; 
      } else { 
        salt = new byte[size]; 
      } 
      RANDOM.nextBytes(salt); 
      return salt; 
    } 

    @Override public byte[] hash(String password, byte[] salt) { 

      Validate.notNull(password, "Password must not be null"); 
      Validate.notNull(salt, "Salt must not be null"); 

      try { 
        final byte[] passwordBytes = password.getBytes("UTF-8"); 
        final byte[] all = ArrayUtils.addAll(passwordBytes, salt); 
        SHA3.DigestSHA3 md = new SHA3.Digest512(); 
        md.update(all); 
        return md.digest(); 
      } catch (UnsupportedEncodingException e) { 
        final String message = String 
          .format("Caught UnsupportedEncodingException e: <%s>", e.getMessage()); 
        LOGGER.error(message); 
      } 
      return new byte[0]; 
    } 

    @Override public boolean isExpectedPassword(final String password, final byte[] salt, final byte[] hash) { 

      Validate.notNull(password, "Password must not be null"); 
      Validate.notNull(salt, "Salt must not be null"); 
      Validate.notNull(hash, "Hash must not be null"); 

      try { 
        final byte[] passwordBytes = password.getBytes("UTF-8"); 
        final byte[] all = ArrayUtils.addAll(passwordBytes, salt); 

        SHA3.DigestSHA3 md = new SHA3.Digest512(); 
        md.update(all); 
        final byte[] digest = md.digest(); 
        return Arrays.equals(digest, hash); 
      }catch(UnsupportedEncodingException e){ 
        final String message = 
          String.format("Caught UnsupportedEncodingException e: <%s>", e.getMessage()); 
        LOGGER.error(message); 
      } 
      return false; 


    } 

    @Override public String generateRandomPassword(final int length) { 

      if (length < 1) { 
        throw new IllegalArgumentException("length must be greater than 0"); 
      } 

      final char[] buf = new char[length]; 
      for (int idx = 0; idx < buf.length; ++idx) { 
        buf[idx] = symbols[RANDOM.nextInt(symbols.length)]; 
      } 
      return shuffle(new String(buf)); 
    } 


    private String shuffle(final String input){ 
      final List<Character> characters = new ArrayList<Character>(); 
      for(char c:input.toCharArray()){ 
        characters.add(c); 
      } 
      final StringBuilder output = new StringBuilder(input.length()); 
      while(characters.size()!=0){ 
        int randPicker = (int)(Math.random()*characters.size()); 
        output.append(characters.remove(randPicker)); 
      } 
      return output.toString(); 
    } 
} 

test durumları:

public class PasswordsTest { 

    private static final Logger LOGGER = Logger.getLogger(PasswordsTest.class); 

    @Before 
    public void setup(){ 
      BasicConfigurator.configure(); 
    } 

    @Test 
    public void testGeSalt() throws Exception { 

      IPasswords passwords = new Passwords(); 
      final byte[] bytes = passwords.getSalt(0); 
      int arrayLength = bytes.length; 

      assertThat("Expected length is", arrayLength, is(64)); 
    } 

    @Test 
    public void testGeSalt32() throws Exception { 
      IPasswords passwords = new Passwords(); 
      final byte[] bytes = passwords.getSalt32(); 
      int arrayLength = bytes.length; 
      assertThat("Expected length is", arrayLength, is(32)); 
    } 

    @Test 
    public void testGeSalt64() throws Exception { 
      IPasswords passwords = new Passwords(); 
      final byte[] bytes = passwords.getSalt64(); 
      int arrayLength = bytes.length; 
      assertThat("Expected length is", arrayLength, is(64)); 
    } 

    @Test 
    public void testHash() throws Exception { 
      IPasswords passwords = new Passwords(); 
      final byte[] hash = passwords.hash("holacomoestas", passwords.getSalt64()); 
      assertThat("Array is not null", hash, Matchers.notNullValue()); 
    } 


    @Test 
    public void testSHA3() throws UnsupportedEncodingException { 
      SHA3.DigestSHA3 md = new SHA3.Digest256(); 
      md.update("holasa".getBytes("UTF-8")); 
      final byte[] digest = md.digest(); 
      assertThat("expected digest is:",digest,Matchers.notNullValue()); 
    } 

    @Test 
    public void testIsExpectedPasswordIncorrect() throws Exception { 

      String password = "givemebeer"; 
      IPasswords passwords = new Passwords(); 

      final byte[] salt64 = passwords.getSalt64(); 
      final byte[] hash = passwords.hash(password, salt64); 
      //The salt and the hash go to database. 

      final boolean isPasswordCorrect = passwords.isExpectedPassword("jfjdsjfsd", salt64, hash); 

      assertThat("Password is not correct", isPasswordCorrect, is(false)); 

    } 

    @Test 
    public void testIsExpectedPasswordCorrect() throws Exception { 
      String password = "givemebeer"; 
      IPasswords passwords = new Passwords(); 
      final byte[] salt64 = passwords.getSalt64(); 
      final byte[] hash = passwords.hash(password, salt64); 
      //The salt and the hash go to database. 
      final boolean isPasswordCorrect = passwords.isExpectedPassword("givemebeer", salt64, hash); 
      assertThat("Password is correct", isPasswordCorrect, is(true)); 
    } 

    @Test 
    public void testGenerateRandomPassword() throws Exception { 
      IPasswords passwords = new Passwords(); 
      final String randomPassword = passwords.generateRandomPassword(10); 
      LOGGER.info(randomPassword); 
      assertThat("Random password is not null", randomPassword, Matchers.notNullValue()); 
    } 
} 

pom.xml (sadece bağımlılıkları):

<dependencies> 
    <dependency> 
     <groupId>junit</groupId> 
     <artifactId>junit</artifactId> 
     <version>4.12</version> 
     <scope>test</scope> 
    </dependency> 
    <dependency> 
     <groupId>org.testng</groupId> 
     <artifactId>testng</artifactId> 
     <version>6.1.1</version> 
     <scope>test</scope> 
    </dependency> 

    <dependency> 
     <groupId>org.hamcrest</groupId> 
     <artifactId>hamcrest-all</artifactId> 
     <version>1.3</version> 
     <scope>test</scope> 
    </dependency> 

    <dependency> 
     <groupId>log4j</groupId> 
     <artifactId>log4j</artifactId> 
     <version>1.2.17</version> 
    </dependency> 

    <dependency> 
     <groupId>org.bouncycastle</groupId> 
     <artifactId>bcprov-jdk15on</artifactId> 
     <version>1.51</version> 
     <type>jar</type> 
    </dependency> 


    <dependency> 
     <groupId>org.apache.commons</groupId> 
     <artifactId>commons-lang3</artifactId> 
     <version>3.3.2</version> 
    </dependency> 


</dependencies>