PBKDF2加密的實現


做過網站的人都知道用戶密碼必須經過加密的,其中用的最普遍的就是MD5加密了.但是隨着彩虹橋技術的興起,MD5加密已經不再安全.

如對於MD5加密來說攻擊者只需要一個簡單的sql語句`:select * from userInfo where password=’4QrcOUm6Wau+VuBX8g+IPg==’` 就可以知道有幾個用戶密碼是”123456”,這對一個項目來說十分危險。

所以一般在加密之前,配上一個一串的隨機序列。稱之為salt。

PBKDF2(Password-Based Key Derivation Function)。

PBKDF2算法通過多次hash來對密碼進行加密。原理是通過password和salt進行hash,然后將結果作為salt在與password進行hash,多次重復此過程,生成最終的密文。此過程可能達到上千次,逆向破解的難度太大,破解一個密碼的時間可能需要幾百年,所以PBKDF2算法是安全的.

密碼加鹽。鹽是一個添加到用戶的密碼哈希過程中的一段隨機序列。這個機制能夠防止通過預先計算結果的彩虹表破解。每個用戶都有自己的鹽,這樣的結果就是即使用戶的密碼相同,通過加鹽后哈希值也將不同。為了校驗密碼是否正確,我們需要儲存鹽值。通常和密碼哈希值一起存放在賬戶數據庫中,或者直接存為哈希字符串的一部分。

PasswordEncryption工具類如下

package com.xianquan.web.util;

import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;

public class PasswordEncryption {

    public static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA1";

    /**
     * 鹽的長度
     */
    public static final int SALT_BYTE_SIZE = 32 / 2;

    /**
     * 生成密文的長度
     */
    public static final int HASH_BIT_SIZE = 128 * 4;

    /**
     * 迭代次數
     */
    public static final int PBKDF2_ITERATIONS = 10000;

    /**
     * 對輸入的密碼進行驗證
     *
     * @param attemptedPassword 待驗證的密碼
     * @param encryptedPassword 密文
     * @param salt 鹽值
     * @return 是否驗證成功
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static boolean authenticate(String attemptedPassword, String encryptedPassword, String salt)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 用相同的鹽值對用戶輸入的密碼進行加密
        String encryptedAttemptedPassword = getEncryptedPassword(attemptedPassword, salt);
        // 把加密后的密文和原密文進行比較,相同則驗證成功,否則失敗
        return encryptedAttemptedPassword.equals(encryptedPassword);
    }

    /**
     * 生成密文
     *
     * @param password 明文密碼
     * @param salt 鹽值
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static String getEncryptedPassword(String password, String salt) throws NoSuchAlgorithmException,
            InvalidKeySpecException {
        KeySpec spec = new PBEKeySpec(password.toCharArray(), fromHex(salt), PBKDF2_ITERATIONS, HASH_BIT_SIZE);
        SecretKeyFactory f = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
        return toHex(f.generateSecret(spec).getEncoded());
    }

    /**
     * 通過提供加密的強隨機數生成器 生成鹽
     *
     * @return
     * @throws NoSuchAlgorithmException
     */
    public static String generateSalt() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        byte[] salt = new byte[SALT_BYTE_SIZE];
        random.nextBytes(salt);
        return toHex(salt);
    }

    /**
     * 十六進制字符串轉二進制字符串
     *
     * @param hex        
     * @return      
     */
    private static byte[] fromHex(String hex) {
        byte[] binary = new byte[hex.length() / 2];
        for (int i = 0; i < binary.length; i++) {
            binary[i] = (byte) Integer.parseInt(hex.substring(2 * i, 2 * i + 2), 16);
        }
        return binary;
    }

    /**
     * 二進制字符串轉十六進制字符串
     *
     * @param array      
     * @return
     */
    private static String toHex(byte[] array) {
        BigInteger bi = new BigInteger(1, array);
        String hex = bi.toString(16);
        int paddingLength = (array.length * 2) - hex.length();
        if (paddingLength > 0){
            return String.format("%0" + paddingLength + "d", 0) + hex;
        } else {
            return hex;
        }
    }
    
}
PasswordEncryption

生成密文:首先要生成一個鹽值salt,再把原始密碼和salt加密得到密文。

驗證密文:把用戶輸入的密碼和同樣的鹽值salt使用相同的加密算法得到一個密文,將這個密文和原密文相比較,相同則驗證通過,反之則不通過。

調用實例如下

    public static void main(String[] args) {
        String password = "admin";
        String salt;
        String ciphertext;
        try {
            salt = PasswordEncryption.generateSalt();
            ciphertext = PasswordEncryption.getEncryptedPassword(password, salt);
            boolean result = PasswordEncryption.authenticate(password, ciphertext, salt);
            System.out.println("password:"+ password + "  " + password.length());
            System.out.println("salt" + salt + "  " + salt.length());
            System.out.println("ciphertext" + ciphertext + "  " + ciphertext.length());
            if (result) {
                System.out.println("succeed");
            } else {
                System.out.println("failed");
            }
        } catch (NoSuchAlgorithmException e) {
            System.out.println("NoSuchAlgorithmException");
        } catch (InvalidKeySpecException e) {
            System.out.println("InvalidKeySpecException");
        }
    }
Test

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM