2012-06-08 42 views
1

我有一個web應用程序,我想在遠程數據庫中存儲密碼散列。我寫了一個簡單的類,它生成密碼哈希。兩個公共方法可供選擇:1。 加密 - 對密碼進行加密,並創建哈希 2. checkPassword - 電話加密創建哈希,然後比較兩個哈希java SecretKeyFactory密碼散列不能正常工作

我做了兩個密碼檢查一個簡單的測試,應該通過,另一個不應該。

有時可行,有時不行。

正確的輸出:

password: abcdefg, hash: fd9927e15150bd01713115a761d1dea18b7da4aa 
password: abcdefg, salt: 3595ac1baff6aa5e0097520593c7ac74 
password: abcdefg, hash: fd9927e15150bd01713115a761d1dea18b7da4aa 
password: abcdefg, salt: 3595ac1baff6aa5e0097520593c7ac74 
passwords: abcdefg and abcdefg matched: true 
password: abcdefgh, hash: a64a2958f3999d8ecdeb03326a151e786435ea4 
password: abcdefgh, salt: 3595ac1baff6aa5e0097520593c7ac74 
passwords: abcdefg and abcdefgh matched: false 

錯誤輸出:

password: abcdefg, hash: 4913fe5cdea3346690463f76f73c1336ae976674 
password: abcdefg, salt: 8e2aa1ec28d84fbaf78a6df260a7c707 
password: abcdefg, hash: 97abd26927bf96076019b932bf6ab5494a8b0979 
password: abcdefg, salt: 8e2aa1ec28d84fbaf78a6df260a7c707 
passwords: abcdefg and abcdefg matched: false 
password: abcdefgh, hash: 70594cd854bd60e07dfe14f72f01aa1f50de9aa2 
password: abcdefgh, salt: 8e2aa1ec28d84fbaf78a6df260a7c707 
passwords: abcdefg and abcdefgh matched: false 

來源:

import java.math.BigInteger; 
import java.security.NoSuchAlgorithmException; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.KeySpec; 
import java.util.Random; 

import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.PBEKeySpec; 

public class CryptoUtils { 

    /*************************************************************************** 
    * @param password 
    * 
    * @return String[2] { hashed password, salt } 
    * 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    ***************************************************************************/ 
    public static String[] encrypt(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     byte[] salt = new byte[16]; 
     new Random().nextBytes(salt); 

     return encrypt(password, salt); 
    } 

    private static String[] encrypt(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 2048, 160); 
     SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
     byte[] hash = f.generateSecret(spec).getEncoded(); 

     String passHash = new BigInteger(1, hash).toString(16); 
     String saltString = new BigInteger(1, salt).toString(16); 

     System.out.println("password: " + password + ", hash: " + passHash);// DEBUG 
     System.out.println("password: " + password + ", salt: " + saltString); 

     return new String[] { passHash, saltString }; 
    } 

    public static boolean checkPassword(String password, String hash, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     String[] encrypted = encrypt(password, new BigInteger(salt, 16).toByteArray()); 

     return encrypted[0].equals(hash); 
    } 

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     String pass1 = "abcdefg"; 
     String pass2 = pass1; 
     String pass3 = pass1 + "h"; 

     String[] result = encrypt(pass1); 
     String hash = result[0]; 
     String salt = result[1]; 

     System.out.println("passwords: " + pass1 + " and " + pass2 + " matched: " + checkPassword(pass2, hash, salt)); 
     System.out.println("passwords: " + pass1 + " and " + pass3 + " matched: " + checkPassword(pass3, hash, salt)); 
    } 
} 

有人能幫忙嗎?

回答

2

問題是您使用BigInteger將salt字符串轉換爲字節。如果你的salt字符串是負數,並且不以零位開始,則此代碼有效。如果salt字符串是正數,則BigInteger.toByteArray()必須通過在起始處添加一個零字節來進行擴展簽名,因此它最多需要17個字節。另外,如果你的Salt字符串的最高位字節爲零,BigInteger.toByteArray()不需要16個字節來表示它,因此你的salt的結尾長度不對。您可能會編寫邏輯來重新格式化BigInteger的輸出,使其始終包含16個字節,但是可能更容易一次只解析輸入字符串兩個字符,並自己將字節值添加到數組中。

+0

謝謝,我使用了Base64.encode()和Base64.decode()方法,它絕對有幫助。 – xMichal

0

問題是在字符串到字節數組和返回轉換。現在我使用Base64.encode()和Base64.decode()方法,它工作。

import java.security.NoSuchAlgorithmException; 
import java.security.spec.InvalidKeySpecException; 
import java.security.spec.KeySpec; 
import java.util.Random; 

import javax.crypto.SecretKeyFactory; 
import javax.crypto.spec.PBEKeySpec; 

import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; 

public class CryptoUtils { 

    /*************************************************************************** 
    * @param password 
    * 
    * @return String[2] { hashed password, salt } 
    * 
    * @throws NoSuchAlgorithmException 
    * @throws InvalidKeySpecException 
    ***************************************************************************/ 
    public static String[] encrypt(String password) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     byte[] salt = new byte[16]; 
     new Random().nextBytes(salt); 

     return encrypt(password, salt); 
    } 

    private static String[] encrypt(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, 2048, 160); 
     SecretKeyFactory f = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 
     byte[] hash = f.generateSecret(spec).getEncoded(); 

     String passHash = Base64.encode(hash); 
     String saltString = Base64.encode(salt); 

     System.out.println("password: " + password + ", hash: " + passHash);// DEBUG 
     System.out.println("password: " + password + ", salt: " + saltString); 

     return new String[] { passHash, saltString }; 
    } 

    public static boolean checkPassword(String password, String hash, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     String[] encrypted = encrypt(password, Base64.decode(salt)); 

     return encrypted[0].equals(hash); 
    } 

    public static void main(String[] args) throws NoSuchAlgorithmException, InvalidKeySpecException { 
     String pass1 = "abcdefg"; 
     String pass2 = pass1; 
     String pass3 = pass1 + "h"; 

     String[] result = encrypt(pass1); 
     String hash = result[0]; 
     String salt = result[1]; 

     System.out.println("passwords: " + pass1 + " and " + pass2 + " matched: " + checkPassword(pass2, hash, salt)); 
     System.out.println("passwords: " + pass1 + " and " + pass3 + " matched: " + checkPassword(pass3, hash, salt)); 
    } 
}