Java Argon2 哈希
Java Argon2 Hashing
我正在尝试弄清楚如何在 Java 中对密码实际使用 argon2 哈希。我必须遗漏一些东西,因为 none 的 API return 散列或盐的离散字段。我已经尝试了 JVM binding for argon2 and also spring-security + bouncy castle 并且都给了我一个字符串,但它也用散列密码和盐之外的信息进行了序列化。
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$i9iHBeHankerOJhfUvXrnQLdr1QkPglW0DSjYqoaoAy0brxs1vPVhlm4174NdR80
如何获取散列和盐的离散值?在我的研究中,听起来我可以自己解析这个输出,但这听起来是个坏主意。
我使用了错误的库吗?我一直在做大量研究,这些是不断出现的两个最受欢迎的库。
我正在使用 Bouncy Castle 来实现 Argon2id,因为它允许设置参数和盐而不是解析输出。
下面的完整 运行 程序使用 4 个参数集 - 参数取自 PHP 的 OpenSSL 实现,但您当然可以单独选择参数。
由于该程序取自跨平台项目,它使用 固定盐,即 UNSECURE - 在生产中您需要使用随机生成的盐。
这是一个输出:
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=
代码:
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);
}
}
我正在尝试弄清楚如何在 Java 中对密码实际使用 argon2 哈希。我必须遗漏一些东西,因为 none 的 API return 散列或盐的离散字段。我已经尝试了 JVM binding for argon2 and also spring-security + bouncy castle 并且都给了我一个字符串,但它也用散列密码和盐之外的信息进行了序列化。
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$i9iHBeHankerOJhfUvXrnQLdr1QkPglW0DSjYqoaoAy0brxs1vPVhlm4174NdR80
如何获取散列和盐的离散值?在我的研究中,听起来我可以自己解析这个输出,但这听起来是个坏主意。
我使用了错误的库吗?我一直在做大量研究,这些是不断出现的两个最受欢迎的库。
我正在使用 Bouncy Castle 来实现 Argon2id,因为它允许设置参数和盐而不是解析输出。
下面的完整 运行 程序使用 4 个参数集 - 参数取自 PHP 的 OpenSSL 实现,但您当然可以单独选择参数。
由于该程序取自跨平台项目,它使用 固定盐,即 UNSECURE - 在生产中您需要使用随机生成的盐。
这是一个输出:
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=
代码:
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);
}
}