Java Argon2哈希

Java Argon2哈希,java,security,spring-security,argon2-ffi,Java,Security,Spring Security,Argon2 Ffi,我正试图弄清楚如何在Java中实际使用argon2哈希作为密码。我肯定遗漏了什么,因为没有一个API返回散列或salt的离散字段。我尝试了a和,它们都给了我一个字符串,但是它也被序列化了,除了散列密码和salt之外的信息 public static void main(String[] args) { final String rawPass = "badPassword"; // argon2-jvm Argon2 argo

我正试图弄清楚如何在Java中实际使用argon2哈希作为密码。我肯定遗漏了什么,因为没有一个API返回散列或salt的离散字段。我尝试了a和,它们都给了我一个字符串,但是它也被序列化了,除了散列密码和salt之外的信息

public static void main(String[] args) {
        final String rawPass = "badPassword";

        // argon2-jvm
        Argon2 argon2jvm = Argon2Factory.create(Argon2Factory.Argon2Types.ARGON2id, 16, 32);
        String arg2JvmHash = argon2jvm.hash(10, 65536, 1, rawPass.getBytes(StandardCharsets.UTF_8));
        System.out.println("argon2-jvm:");
        System.out.println(arg2JvmHash);
        System.out.println("\n\n");

        // spring security + bouncy castle
        Argon2PasswordEncoder arg2SpringSecurity = new Argon2PasswordEncoder(16, 32, 1, 65536, 10);
        String springBouncyHash = arg2SpringSecurity.encode(rawPass);
        System.out.println("spring security + bouncy castle:");
        System.out.println(springBouncyHash);
        System.out.println("\n\n");
}
下面是结果:

argon2-jvm:
$argon2id$v=19$m=65536,t=10,p=1$BeHo0SdgM6vt5risz+yuLg$dOBFlfeoPPGCk/OLCGJ9sRhyPl0zMqMAUZvkltFWxnA



spring security + bouncy castle:
$argon2id$v=19$m=65536,t=10,p=1$i9iHBeHankerOJhfUvXrnQ$8Ldr1QkPglW0DSjYqoaoAy0brxs1vPVhlm4174NdR80
如何获得散列和盐的离散值?在我的研究中,听起来我可以自己解析这个输出,但这听起来是个坏主意


我用错图书馆了吗?我已经做了很多研究,这两个最流行的库不断出现。

我正在使用Bouncy Castle实现Argon2id,因为它允许设置参数和salt,而不是解析输出

下面完整运行的程序使用4个参数集-参数取自PHP的OpenSSL实现,但您当然可以单独选择参数

由于该计划来自一个跨平台项目,它使用了一种不安全的固定盐,在生产中,您需要使用随机生成的盐

这是一个输出:

Generate a 32 byte long encryption key with Argon2id
password: secret password
salt (Base64): AAAAAAAAAAAAAAAAAAAAAA==
encryptionKeyArgon2id (Base64) minimal:     e9G7+HHmftUaCEP2O1NwCSJkfyAT0QBzod3Szm1elf0=
encryptionKeyArgon2id (Base64) interactive: FZcsUwo7wf7V24qWTwKeSN9//+Pxy2gCKN35KZX2hXs=
encryptionKeyArgon2id (Base64) moderate:    gdizE6kia1W/CgTA3bRKKjtaf8cgZL1BIe6jeDegg0c=
encryptionKeyArgon2id (Base64) sensitive:   19Uym9wI6e/l5f0NocZmNEaouoHvsSyVfrp9iRYl/C8=
代码:


很有趣,谢谢你的详细介绍!如果我理解正确,我需要使用
Argon2BytesGenerator
类,而不是
Argon2PasswordEncoder
类?是的,Argon2BytesGenerator是您需要的。很酷,刚刚有机会测试一下。在我看来,这似乎是赢家!我还注意到散列总是以
=
结尾。你知道为什么吗?这是一件值得关注的事情吗?一点也不担心——因为我正在用Base64编码输出。由于Base64编码,32字节长的输出(加密密钥)变长,“=”是一种“填充”。由于所有输入长度均为32字节,因此填充始终为一“=”。
import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters;

import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.Base64;

public class Argon2id {
    public static void main(String[] args) {
        // uses Bouncy Castle
        System.out.println("Generate a 32 byte long encryption key with Argon2id");

        String password = "secret password";
        System.out.println("password: " + password);

        // ### security warning - never use a fixed salt in production, this is for compare reasons only
        byte[] salt = generateFixedSalt16Byte();
        // please use below generateSalt16Byte()
        //byte[] salt = generateSalt16Byte();
        System.out.println("salt (Base64): " + base64Encoding(salt));

        // ### the minimal parameter set is probably UNSECURE ###
        String encryptionKeyArgon2id = base64Encoding(generateArgon2idMinimal(password, salt));
        System.out.println("encryptionKeyArgon2id (Base64) minimal:     " + encryptionKeyArgon2id);

        encryptionKeyArgon2id = base64Encoding(generateArgon2idInteractive(password, salt));
        System.out.println("encryptionKeyArgon2id (Base64) interactive: " + encryptionKeyArgon2id);

        encryptionKeyArgon2id = base64Encoding(generateArgon2idModerate(password, salt));
        System.out.println("encryptionKeyArgon2id (Base64) moderate:    " + encryptionKeyArgon2id);

        encryptionKeyArgon2id = base64Encoding(generateArgon2idSensitive(password, salt));
        System.out.println("encryptionKeyArgon2id (Base64) sensitive:   " + encryptionKeyArgon2id);
    }

    // ### the minimal parameter set is probably UNSECURE ###
    public static byte[] generateArgon2idMinimal(String password, byte[] salt) {
        int opsLimit = 2;
        int memLimit = 8192;
        int outputLength = 32;
        int parallelism = 1;
        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
                .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
                .withIterations(opsLimit)
                .withMemoryAsKB(memLimit)
                .withParallelism(parallelism)
                .withSalt(salt);
        Argon2BytesGenerator gen = new Argon2BytesGenerator();
        gen.init(builder.build());
        byte[] result = new byte[outputLength];
        gen.generateBytes(password.getBytes(StandardCharsets.UTF_8), result, 0, result.length);
        return result;
    }

    public static byte[] generateArgon2idInteractive(String password, byte[] salt) {
        int opsLimit = 2;
        int memLimit = 66536;
        int outputLength = 32;
        int parallelism = 1;
        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
                .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
                .withIterations(opsLimit)
                .withMemoryAsKB(memLimit)
                .withParallelism(parallelism)
                .withSalt(salt);
        Argon2BytesGenerator gen = new Argon2BytesGenerator();
        gen.init(builder.build());
        byte[] result = new byte[outputLength];
        gen.generateBytes(password.getBytes(StandardCharsets.UTF_8), result, 0, result.length);
        return result;
    }

    public static byte[] generateArgon2idModerate(String password, byte[] salt) {
        int opsLimit = 3;
        int memLimit = 262144;
        int outputLength = 32;
        int parallelism = 1;
        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
                .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
                .withIterations(opsLimit)
                .withMemoryAsKB(memLimit)
                .withParallelism(parallelism)
                .withSalt(salt);
        Argon2BytesGenerator gen = new Argon2BytesGenerator();
        gen.init(builder.build());
        byte[] result = new byte[outputLength];
        gen.generateBytes(password.getBytes(StandardCharsets.UTF_8), result, 0, result.length);
        return result;
    }

    public static byte[] generateArgon2idSensitive(String password, byte[] salt) {
        int opsLimit = 4;
        int memLimit = 1048576;
        int outputLength = 32;
        int parallelism = 1;
        Argon2Parameters.Builder builder = new Argon2Parameters.Builder(Argon2Parameters.ARGON2_id)
                .withVersion(Argon2Parameters.ARGON2_VERSION_13) // 19
                .withIterations(opsLimit)
                .withMemoryAsKB(memLimit)
                .withParallelism(parallelism)
                .withSalt(salt);
        Argon2BytesGenerator gen = new Argon2BytesGenerator();
        gen.init(builder.build());
        byte[] result = new byte[outputLength];
        gen.generateBytes(password.getBytes(StandardCharsets.UTF_8), result, 0, result.length);
        return result;
    }

    private static byte[] generateSalt16Byte() {
        SecureRandom secureRandom = new SecureRandom();
        byte[] salt = new byte[16];
        secureRandom.nextBytes(salt);
        return salt;
    }

    private static byte[] generateFixedSalt16Byte() {
        // ### security warning - never use this in production ###
        byte[] salt = new byte[16]; // 16 x0's
        return salt;
    }

    private static String base64Encoding(byte[] input) {
        return Base64.getEncoder().encodeToString(input);
    }
}