integrate-sha512-password-hashing #68
@ -66,7 +66,7 @@ dependencies {
|
||||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.17.0'
|
||||
implementation 'org.openapitools:jackson-databind-nullable:0.2.6'
|
||||
implementation 'org.apache.commons:commons-text:1.11.0'
|
||||
implementation 'org.bouncycastle:bcpkix-jdk18on:1.76'
|
||||
implementation 'net.java.dev.jna:jna:5.8.0'
|
||||
implementation 'org.modelmapper:modelmapper:3.2.0'
|
||||
implementation 'org.iban4j:iban4j:3.2.7-RELEASE'
|
||||
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.4.0'
|
||||
|
@ -6,8 +6,8 @@ import java.util.PriorityQueue;
|
||||
import java.util.Queue;
|
||||
import java.util.random.RandomGenerator;
|
||||
|
||||
import org.bouncycastle.crypto.generators.OpenBSDBCrypt;
|
||||
|
||||
import com.sun.jna.Library;
|
||||
import com.sun.jna.Native;
|
||||
|
||||
public class LinuxEtcShadowHashGenerator {
|
||||
|
||||
@ -15,13 +15,13 @@ public class LinuxEtcShadowHashGenerator {
|
||||
private static final Queue<String> predefinedSalts = new PriorityQueue<>();
|
||||
|
||||
public static final int SALT_LENGTH = 16;
|
||||
public static final int COST_FACTOR = 13;
|
||||
|
||||
private final String plaintextPassword;
|
||||
private Algorithm algorithm;
|
||||
|
||||
public enum Algorithm {
|
||||
SHA512("6");
|
||||
SHA512("6"),
|
||||
YESCRYPT("y");
|
||||
|
||||
final String prefix;
|
||||
|
||||
@ -57,12 +57,12 @@ public class LinuxEtcShadowHashGenerator {
|
||||
|
||||
void verify(final String givenHash) {
|
||||
final var parts = givenHash.split("\\$");
|
||||
if (parts.length != 4) {
|
||||
if (parts.length < 3 || parts.length > 5) {
|
||||
throw new IllegalArgumentException("not a " + algorithm.name() + " Linux hash: " + givenHash);
|
||||
}
|
||||
|
||||
algorithm = Algorithm.byPrefix(parts[1]);
|
||||
salt = parts[2];
|
||||
salt = parts.length == 4 ? parts[2] : parts[2] + "$" + parts[3];
|
||||
|
||||
if (!generate().equals(givenHash)) {
|
||||
throw new IllegalArgumentException("invalid password");
|
||||
@ -76,9 +76,8 @@ public class LinuxEtcShadowHashGenerator {
|
||||
if (plaintextPassword == null) {
|
||||
throw new IllegalStateException("no password given");
|
||||
}
|
||||
final var hash = OpenBSDBCrypt.generate(plaintextPassword.toCharArray(), salt.getBytes(), COST_FACTOR);
|
||||
final var hashedPassword = new String(org.bouncycastle.util.encoders.Base64.encode(hash.getBytes()));
|
||||
return "$" + algorithm.prefix + "$" + salt + "$" + hashedPassword;
|
||||
|
||||
return NativeCryptLibrary.INSTANCE.crypt(plaintextPassword, "$" + algorithm.prefix + "$" + salt);
|
||||
}
|
||||
|
||||
public static void nextSalt(final String salt) {
|
||||
@ -101,4 +100,13 @@ public class LinuxEtcShadowHashGenerator {
|
||||
}
|
||||
return withSalt(stringBuilder.toString());
|
||||
}
|
||||
public static void main(String[] args) {
|
||||
System.out.println(NativeCryptLibrary.INSTANCE.crypt("given password", "$6$abcdefghijklmno"));
|
||||
}
|
||||
|
||||
public interface NativeCryptLibrary extends Library {
|
||||
NativeCryptLibrary INSTANCE = Native.load("crypt", NativeCryptLibrary.class);
|
||||
|
||||
String crypt(String password, String salt);
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,20 @@ class LinuxEtcShadowHashGeneratorUnitTest {
|
||||
final String WRONG_PASSWORD = "wrong password";
|
||||
final String GIVEN_SALT = "0123456789abcdef";
|
||||
|
||||
// generated via mkpasswd for plaintext password GIVEN_PASSWORD (see above)
|
||||
final String GIVEN_SHA512_HASH = "$6$ooei1HK6JXVaI7KC$sY5d9fEOr36hjh4CYwIKLMfRKL1539bEmbVCZ.zPiH0sv7jJVnoIXb5YEefEtoSM2WWgDi9hr7vXRe3Nw8zJP/";
|
||||
final String GIVEN_YESCRYPT_HASH = "$y$j9T$wgYACPmBXvlMg2MzeZA0p1$KXUzd28nG.67GhPnBZ3aZsNNA5bWFdL/dyG4wS0iRw7";
|
||||
|
||||
@Test
|
||||
void verifiesPasswordAgainstSha512HashFromMkpasswd() {
|
||||
hash(GIVEN_PASSWORD).verify(GIVEN_SHA512_HASH); // throws exception if wrong
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifiesPasswordAgainstYescryptHashFromMkpasswd() {
|
||||
hash(GIVEN_PASSWORD).verify(GIVEN_YESCRYPT_HASH); // throws exception if wrong
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifiesHashedPasswordWithRandomSalt() {
|
||||
final var hash = hash(GIVEN_PASSWORD).using(SHA512).withRandomSalt().generate();
|
||||
|
Loading…
Reference in New Issue
Block a user