From 9403d35d475df65347f924ef6144b23718902102 Mon Sep 17 00:00:00 2001 From: Stephen Nimmo Date: Wed, 15 Sep 2021 09:12:41 -0500 Subject: [PATCH] Added a matches method to BcryptUtil.java to facilitate the checking of plain text passwords against an existing bcrypt hash --- .../elytron/security/common/BcryptUtil.java | 24 +++++++++++++++++++ .../common/runtime/BcryptUtilTest.java | 14 +++++++++++ 2 files changed, 38 insertions(+) diff --git a/extensions/elytron-security-common/runtime/src/main/java/io/quarkus/elytron/security/common/BcryptUtil.java b/extensions/elytron-security-common/runtime/src/main/java/io/quarkus/elytron/security/common/BcryptUtil.java index 061c697af7041..5fbdf5a7d97be 100644 --- a/extensions/elytron-security-common/runtime/src/main/java/io/quarkus/elytron/security/common/BcryptUtil.java +++ b/extensions/elytron-security-common/runtime/src/main/java/io/quarkus/elytron/security/common/BcryptUtil.java @@ -1,10 +1,12 @@ package io.quarkus.elytron.security.common; +import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; import java.security.spec.InvalidKeySpecException; import java.util.Objects; +import org.wildfly.security.password.Password; import org.wildfly.security.password.PasswordFactory; import org.wildfly.security.password.WildFlyElytronPasswordProvider; import org.wildfly.security.password.interfaces.BCryptPassword; @@ -89,4 +91,26 @@ public static String bcryptHash(String password, int iterationCount, byte[] salt throw new RuntimeException(e); } } + + /** + * Matches a plain text string against an existing Modular Crypt Format bcrypt hash + * + * @param plainText the plain text string to check + * @param passwordHash the Modular Crypt Format bcrypt hash to compare against + * @return the boolean result of whether or not the plain text matches the decoded Modular Crypt Format bcrypt hash + * @throws NullPointerException if the plainText password or passwordHash is null + */ + public static boolean matches(String plainText, String passwordHash) { + Objects.requireNonNull(plainText, "plainText password is required"); + Objects.requireNonNull(passwordHash, "passwordHash is required"); + try { + PasswordFactory passwordFactory = PasswordFactory.getInstance(BCryptPassword.ALGORITHM_BCRYPT, provider); + Password userPasswordDecoded = ModularCrypt.decode(passwordHash); + Password userPasswordRestored = passwordFactory.translate(userPasswordDecoded); + return passwordFactory.verify(userPasswordRestored, plainText.toCharArray()); + } catch (NoSuchAlgorithmException | InvalidKeySpecException | InvalidKeyException e) { + // can't really happen + throw new RuntimeException(e); + } + } } diff --git a/extensions/elytron-security-common/runtime/src/test/java/io/quarkus/elytron/security/common/runtime/BcryptUtilTest.java b/extensions/elytron-security-common/runtime/src/test/java/io/quarkus/elytron/security/common/runtime/BcryptUtilTest.java index 7a66dfd4dcad0..adf19055f9e7c 100644 --- a/extensions/elytron-security-common/runtime/src/test/java/io/quarkus/elytron/security/common/runtime/BcryptUtilTest.java +++ b/extensions/elytron-security-common/runtime/src/test/java/io/quarkus/elytron/security/common/runtime/BcryptUtilTest.java @@ -39,4 +39,18 @@ public void testHashesTheSameHash() throws InvalidKeySpecException, NoSuchAlgori String adminProducedBcrypt = BcryptUtil.bcryptHash("admin", 10, knownSalt); Assertions.assertEquals(adminKnownBcrypt, adminProducedBcrypt); } + + @Test + public void testPasswordMatches() { + String testPassword = "fubar"; + String testPasswordHash = BcryptUtil.bcryptHash(testPassword); + Assertions.assertTrue(BcryptUtil.matches(testPassword, testPasswordHash)); + } + + @Test + public void testPasswordNotMatches() { + String testPassword = "fubar"; + String testPasswordHash = BcryptUtil.bcryptHash(testPassword); + Assertions.assertFalse(BcryptUtil.matches("fubar2", testPasswordHash)); + } }