PBKDF2加密算法集成

        PBKDF2通过password和salt进行hash,然后将结果作为salt在与password进行hash,多次重复此过程,生成最终的密文。

package eu.digient.sdk.security

import eu.digient.sdk.util.AssertUtil
import eu.digient.sdk.util.Base64Util

import javax.crypto.SecretKeyFactory
import javax.crypto.spec.PBEKeySpec
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom

/**
 * Encrypt password with PBKDF2 algorithm
 */
class PBKDF2Helper {
    static final int SALT_SIZE = 16
    private static final int ITERATION_TIMES = 2000
    private static final int ENCODED_BYTES_LENGTH = 64 * 8
    private static final String SHA1PRNG = "SHA1PRNG"

    /**
     * Match password
     * @param rawPassword - input password
     * @param encryptedPassword - encrypted password
     * @return
     */
    static Boolean matches(final String rawPassword, final String encryptedPassword) {
        AssertUtil.notNull(rawPassword, 'rawPassword is required')
        AssertUtil.notNull(encryptedPassword, 'encryptedPassword is required')

        byte[] decodedPwd = Base64Util.decode(encryptedPassword)
        byte[] salt = truncateBytes(decodedPwd, SALT_SIZE)

        return encryptedPassword == encryptWithPBKDF2(rawPassword, salt)
    }

    /**
     * Encrypt password with PBKDF2 algorithm.
     * @param rawPassword - password to be encoded
     * @param salt - randomly generated salt array
     * @return
     */
    static String encryptWithPBKDF2(final String rawPassword, final byte[] salt) {
        AssertUtil.notNull(rawPassword, 'password is required')
        AssertUtil.notNull(salt, 'salt is required')
        if (salt.length != SALT_SIZE) {
            throw new IllegalArgumentException("salt size must be ${SALT_SIZE}")
        }

        SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(Pbkdf2Algorithm.PBKDF2WithHmacSHA256.name())
        PBEKeySpec keySpec = new PBEKeySpec(rawPassword.toCharArray(), salt, ITERATION_TIMES, ENCODED_BYTES_LENGTH)

        byte[] encodedPwd = keyFactory.generateSecret(keySpec).getEncoded()
        byte[] combineWithSalt = concatBytes(salt, encodedPwd)

        return Base64Util.encodeBytesToString(combineWithSalt)
    }

    /**
    * generate random salt arrays
    *
    */
    static byte[] generateSalt() throws NoSuchAlgorithmException {
        SecureRandom random = SecureRandom.getInstance(SHA1PRNG)
        byte[] bytes = new byte[SALT_SIZE]
        random.nextBytes(bytes)
        return bytes
    }

    /**
     * Concatenate two byte arrays into one array
     * @param bytes1
     * @param bytes2
     * @return
     */
    private static byte[] concatBytes(final byte[] bytes1, final byte[] bytes2) {
        int length = bytes1.length + bytes2.length

        byte[] targetBytes = new byte[length]
        System.arraycopy(bytes1, 0, targetBytes, 0, bytes1.length)
        System.arraycopy(bytes2, 0, targetBytes, bytes1.length, bytes2.length)
        return targetBytes
    }

    /**
     * Truncate bytes array by length
     * @param originBytes - to be truncated byte array
     * @param length - truncate length
     * @return
     */
    private static byte[] truncateBytes(final byte[] originBytes, final int length) {
        byte[] newBytes = new byte[length]
        System.arraycopy(originBytes, 0, newBytes, 0, length)
        return newBytes
    }

    enum Pbkdf2Algorithm {
        PBKDF2WithHmacSHA1,
        PBKDF2WithHmacSHA256,
        PBKDF2WithHmacSHA512
    }
}

 test:

class PBKDF2HelperTest {
    @Test void matches() {
        assertTrue(PBKDF2Helper.matches("some-password", "kfR92MqruX2UcChISEyKIx1OTtaPbMBPdMRFQD8q2e/RDqLckSzDUUG5RXBbVsAF1qjh7qw9XKwiUpoUaGc1didsMbsHXpW/2kaxIShWVo8="))
        assertTrue(PBKDF2Helper.matches("some-password1", "E2D+neHoQhPNhDO2pob8NGlZZ0w10I+4/LmsjVeS85YME8b+CZPhSIRcnDq05WcVRxGyw0C1CGZ/A1RUaLhORlaNcN9Jo4nlYKtuw1wgJQA="))
        assertTrue(PBKDF2Helper.matches("some-password2", "5rnJlt+677BCMGn53SPM3+Winuc2+96P21bUrA+EjuWpnOSwMZfs2F56W+ol/0TUsO2H60Pm6X9K1UN2oaCIXb4w7WkeudUe9s+g4VTqDSk="))
        assertTrue(PBKDF2Helper.matches("some-password3", "i/Cek42KWwGBSvKq4Uv7jXqYa5DtcfluTEU9qdMArIRqFSG6RbIRXymtDDMK1NfzAWkxciDj3U5UWbBzANQjFVCmTWEKJL03n+/odHBbjO8="))
        assertTrue(PBKDF2Helper.matches("some-password4", "zXpJftOhs6Tk9FTW8LbsXC9XnQeq7FQJfquKNkQknf8DofKpTEN9zTuDuccVM7AJbl1+mZoG0Fc7xyaVZ4/qlB2TUFZFj/k8onRqQ8ht6BU="))
    }

    @Test void encryptPassword() {
        byte[] salt = PBKDF2Helper.generateSalt()
        String encodedPwd = PBKDF2Helper.encryptWithPBKDF2("some-password", salt)
        assertNotNull(encodedPwd)

        byte[] salt2 = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
        assertEquals("AAECAwQFBgcICQoLDA0ODx273j5Q3RO9lgARLKmwYL/TGYNMd8D6sDFrOJ7mult6A2fc3JgFne8R0sf2pP11YnOAnBBsz/id/tzLbvKmEFg=", PBKDF2Helper.encryptWithPBKDF2("some-password", salt2))
    }

    @Test void generateSalt() {
        for (int i = 0; i < 100; i++) {
            byte[] r1 = PBKDF2Helper.generateSalt()
            byte[] r2 = PBKDF2Helper.generateSalt()

            assertNotEquals(r1, r2)
            assertEquals(PBKDF2Helper.SALT_SIZE, r1.size())
        }
    }
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值