java SecretKeyFactory密码哈希不';我不能正常工作

java SecretKeyFactory密码哈希不';我不能正常工作,java,security,hash,passwords,salt,Java,Security,Hash,Passwords,Salt,我有一个web应用程序,我想在远程数据库中存储密码哈希。我编写了一个简单的类,它生成一个密码散列。有两种公共方法可用: 1.加密-加密密码并创建哈希 2.checkPassword-调用encrypt创建散列,然后比较两个散列 我用两个密码检查做了一个简单的测试,一个应该通过,另一个不应该 有时有效,有时无效 正确输出: password: abcdefg, hash: fd9927e15150bd01713115a761d1dea18b7da4aa password: abcdefg, sal

我有一个web应用程序,我想在远程数据库中存储密码哈希。我编写了一个简单的类,它生成一个密码散列。有两种公共方法可用: 1.加密-加密密码并创建哈希 2.checkPassword-调用encrypt创建散列,然后比较两个散列

我用两个密码检查做了一个简单的测试,一个应该通过,另一个不应该

有时有效,有时无效

正确输出:

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

有人能帮忙吗?

问题是您使用BigInteger将salt字符串转换为字节。如果您的salt字符串是负数,并且不是以零位开头,则此代码有效。如果salt字符串为正,则BigInteger.toByteArray()必须通过在开头再添加一个零字节来对扩展进行签名,因此它的长度为17字节。此外,如果salt字符串的最高顺序字节为零,则biginger.toByteArray()不需要16个字节来表示它,因此salt的长度也会错误。您可能可以编写逻辑来重新格式化BigInteger的输出,使其始终包含16个字节,但只需一次解析输入字符串两个字符,自己将字节值添加到数组中,可能会更容易。

问题在于字符串到字节数组的转换和反向转换。现在我使用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));
    }
}

谢谢,我使用了Base64.encode()和Base64.decode()方法,它确实很有用。