Search code examples
javawildflybcryptquarkus

Which version of bcrypt is used in Quarkus and how to generate valid hashes?


We are currently migrating our application from payara to Quarkus and having trouble with the bCrypt mapper.
We cannot properly generate correct hashes because we do not know which version of bCrypt is used. We tried with the example hashes from your github and that works fine, but when we try to use our own generated hashes it doesn't.
We tried using multiple online generators but we cannot find out which version is used in Quarkus. Then we tried to use the wildfly generator code snippet (see below), because we thought that would use the same algorithm as quarkus does, but that also didn't work.

static final Provider ELYTRON_PROVIDER = new WildFlyElytronProvider();

    static final String TEST_PASSWORD = "test";

    public static void main(String[] args) {

        PasswordFactory passwordFactory = null;
        try {
            passwordFactory = PasswordFactory.getInstance(BCryptPassword.ALGORITHM_BCRYPT, ELYTRON_PROVIDER);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }

        int iterationCount = 10;

        byte[] salt = new byte[BCryptPassword.BCRYPT_SALT_SIZE];
        SecureRandom random = new SecureRandom();
        random.nextBytes(salt);

        IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(iterationCount, salt);
        EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);

        BCryptPassword original = null;
        try {
            original = (BCryptPassword) passwordFactory.generatePassword(encryptableSpec);
        } catch (InvalidKeySpecException e) {
            e.printStackTrace();
        }

        byte[] hash = original.getHash();

        Base64.Encoder encoder = Base64.getEncoder();
        System.out.println("Encoded Salt = " + encoder.encodeToString(salt));
        System.out.println("Encoded Hash = " + encoder.encodeToString(hash));
    }

When we switch from bCrypt to clear it works fine as well, which means the database works fine. Since the example hashes worked fine we do not think the configuration is wrong, but here is our config just in case:

# h2 datasource
#quarkus.datasource.url = jdbc:h2:mem:test
#quarkus.datasource.driver = org.h2.Driver
#quarkus.datasource.username = sa
#quarkus.datasource.password =

# postgres configuration
quarkus.datasource.url = jdbc:postgresql://localhost:5432/postgres
quarkus.datasource.driver = org.postgresql.Driver
quarkus.datasource.username = postgres
quarkus.datasource.password = postgres

#Authorisierung
quarkus.http.auth.basic=true
quarkus.security.jdbc.realm-name=quarkus
quarkus.security.jdbc.enabled=true
quarkus.security.jdbc.principal-query.sql=SELECT n.hash, n.salt, n.iterations FROM nutzer n WHERE n.nutzername=?
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.enabled=true
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.password-index=1
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.salt-index=2
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.iteration-count-index=3
quarkus.security.jdbc.principal-query.bcrypt-password-mapper.hash-encoding=BASE64

quarkus.security.jdbc.principal-query.roles.sql=SELECT nb.berechtigungen_name FROM nutzer n JOIN nutzer_berechtigung nb ON nb.nutzers_nutzername = n.nutzername WHERE n.nutzername=?
quarkus.security.jdbc.principal-query.roles.datasource=permissions
quarkus.security.jdbc.principal-query.roles.attribute-mappings.0.index=1
quarkus.security.jdbc.principal-query.roles.attribute-mappings.0.to=groups

# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation=drop-and-create

#Always include swagger-ui
quarkus.swagger-ui.always-include=true

We do not get an error message, we just can't authenticate an user.

Are we making a mistake in our config or are we using the wrong algorithm or version of bCrypt?


Solution

  • You can use the example from wildfly github to generate a valid hash and salt:

    static final Provider ELYTRON_PROVIDER = new WildFlyElytronPasswordProvider();
    static final String TEST_PASSWORD = "myPassword";
    
    public static void main(String[] args) throws Exception {
        PasswordFactory passwordFactory = PasswordFactory.getInstance(BCryptPassword.ALGORITHM_BCRYPT, ELYTRON_PROVIDER);
    
        int iterationCount = 10;
    
        byte[] salt = new byte[BCryptPassword.BCRYPT_SALT_SIZE];
        SecureRandom random = new SecureRandom();
        random.nextBytes(salt);
    
        IteratedSaltedPasswordAlgorithmSpec iteratedAlgorithmSpec = new IteratedSaltedPasswordAlgorithmSpec(iterationCount, salt);
        EncryptablePasswordSpec encryptableSpec = new EncryptablePasswordSpec(TEST_PASSWORD.toCharArray(), iteratedAlgorithmSpec);
    
        BCryptPassword original = (BCryptPassword) passwordFactory.generatePassword(encryptableSpec);
    
        byte[] hash = original.getHash();
    
        Encoder encoder = Base64.getEncoder();
        System.out.println("Encoded Salt = " + encoder.encodeToString(salt));
        System.out.println("Encoded Hash = " + encoder.encodeToString(hash));
    }
    

    Note that WildFlyElytronProvider() is deprecated so far, so you have to use WildFlyElytronPasswordProvider() instead.