如何在数据库中保留指定长度的盐

How to persist a salt with specified lenght in the database

我已经有一个包含 table 命名用户的数据库,此 table 包含 table 盐、密码等...

  1. salt 列有长度为 43 的数据
  2. 密码栏有88位长度的数据

我的数据库是由 symfony(ORM+FOSUserBundle) 他使用 sha512 创建的。

我正在尝试获取盐和密码,并将它们从桌面 java 应用程序存储到数据库中,所以我尝试了这个 Class:

import com.google.common.io.BaseEncoding;
import org.slf4j.Logger;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import com.google.common.primitives.Bytes;
import org.slf4j.LoggerFactory;

public class SHA512 {
    private static final Logger log = LoggerFactory.getLogger(SHA512.class);
    private static final String ALGORITHM = "SHA-512";
    private static final int ITERATIONS = 5000;
    private static final int SALT_SIZE = 43;


    /**
     * Private constructor.
     */
    private SHA512() {
    }

    public static void main(String[] args) {
        String password = "0000";

        try {

            byte[] salt = generateSalt();
            log.info("Password {}. hash algorithm {}, iterations {}, salt {}", password, ALGORITHM, ITERATIONS,
                    BaseEncoding.base64().encode(salt));
            byte[] hash = calculateHash(password, salt);
            boolean correct = verifyPassword(hash, password, salt);

            log.info("Entered password is correct: {}", correct);
        } catch (NoSuchAlgorithmException | UnsupportedEncodingException ex) {
            log.error(ex.getMessage(), ex);
        }
    }


    private static byte[] generateSalt() {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[SALT_SIZE];
        random.nextBytes(salt);

        return salt;
    }

    private static byte[] calculateHash(String password, byte[] salt) throws NoSuchAlgorithmException,
            UnsupportedEncodingException {
        MessageDigest md = MessageDigest.getInstance(ALGORITHM);
        md.reset();
        md.update(Bytes.concat(password.getBytes("UTF-8"), salt));
        byte[] hash = md.digest();

        for (int i = 0; i < ITERATIONS; i++) {
            md.reset();
            hash = md.digest(hash);
        }
        return hash;
    }

    private static boolean verifyPassword(byte[] originalHash, String password, byte[] salt) throws
            NoSuchAlgorithmException, UnsupportedEncodingException {
        byte[] comparisonHash = calculateHash(password, salt);

        log.info("hash 1: {}", BaseEncoding.base64().encode(originalHash));
        log.info("hash 2: {}", BaseEncoding.base64().encode(comparisonHash));

        return comparePasswords(originalHash, comparisonHash);
    }

    /**
     * Compares the two byte arrays in length-constant time using XOR.
     *
     * @param originalHash   The original password hash
     * @param comparisonHash The comparison password hash
     * @return True if both match, false otherwise
     */
    private static boolean comparePasswords(byte[] originalHash, byte[] comparisonHash) {
        int diff = originalHash.length ^ comparisonHash.length;
        for (int i = 0; i < originalHash.length && i < comparisonHash.length; i++) {
            diff |= originalHash[i] ^ comparisonHash[i];
        }

        return diff == 0;
    }
}

我需要一个长度为 43 的盐,但是 BaseEncoding.base64().encode(salt) 输出长度为 60 的盐。 我将 SALT_SIZE 修改为 30 并且 BaseEncoding.base64().encode(salt) 输出一个长度等于 40 的盐,但是,当我添加一个具有该盐并生成哈希的用户时,我无法从我的用户登录web 应用程序(已经说过 FOSUserBundle 控制身份验证和 encode/decode 算法)

如果我没看错,那么你的摘要编码器实现有误。

参考点:

MessageDigestPasswordEncoder.php

BasePasswordEncoder.php

当您请求手动生成摘要时,基本上有两个步骤:

  1. 哈希连接:password + { + salt + }
  2. 对于每个额外的迭代:散列 previous digest + salt 的连接(请注意,此步骤不添加 {} 个字符)

所以,我在您的实施中发现了两个问题:

  1. 在我看来,您没有在迭代 #0
  2. 中包含那些 {}
  3. 您没有在以后的任何迭代中包含盐

希望对您有所帮助...