2

So I've created a database which stores user information and their hashed password, which is hashed using a Python script using the bcrypt module.

Now to login using my Java client, I am using the JBCrypt library included as a Maven dependency in my IntelliJ project. It retrieves the hash and the database connection works perfectly, the only issue is that it throws this error:

Exception in thread "AWT-EventQueue-0" java.lang.IllegalArgumentException: Invalid salt revision
    at org.mindrot.jbcrypt.BCrypt.hashpw(BCrypt.java:671)
    at org.mindrot.jbcrypt.BCrypt.checkpw(BCrypt.java:763)
    at mypackage.Login.validateUser(Login.java:81)
    at mypackage.Login.actionPerformed(Login.java:63)
    at java.desktop/javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1967)
    at java.desktop/javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2308)
    at java.desktop/javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:405)
    at java.desktop/javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:262)
    at java.desktop/javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:269)
    at java.desktop/java.awt.Component.processMouseEvent(Component.java:6578)
    at java.desktop/javax.swing.JComponent.processMouseEvent(JComponent.java:3343)
    at java.desktop/java.awt.Component.processEvent(Component.java:6343)
    at java.desktop/java.awt.Container.processEvent(Container.java:2259)
    at java.desktop/java.awt.Component.dispatchEventImpl(Component.java:4961)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2317)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4793)
    at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4904)
    at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Container.java:4539)
    at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Container.java:4480)
    at java.desktop/java.awt.Container.dispatchEventImpl(Container.java:2303)
    at java.desktop/java.awt.Window.dispatchEventImpl(Window.java:2758)
    at java.desktop/java.awt.Component.dispatchEvent(Component.java:4793)
    at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:766)
    at java.desktop/java.awt.EventQueue.access$500(EventQueue.java:97)
    at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:717)
    at java.desktop/java.awt.EventQueue$3.run(EventQueue.java:711)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:89)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:99)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:739)
    at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:737)
    at java.base/java.security.AccessController.doPrivileged(Native Method)
    at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:89)
    at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:736)
    at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:199)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
    at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
    at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
    at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)

The code for checking is as follows:

String dbHash = results.getString("password");
System.out.println(dbHash);
boolean result = BCrypt.checkpw(new String(passwordField.getPassword()), dbHash);

EDIT: An example of the hashed password and raw password from the Python module script is as follows:

Raw:    zO/15;w|c'*uftH)
Hashed: $2b$12$7Y4ZmORuoH0dziYSg8dpd.PfvWQx2QPcoiRoGWg.HKfhuN6evEZnO

**EDIT: ** I have noticed it is because JBCrypt does not seem to support anything greater than $2a hashes. I cannot find the bcrypt module documentation for Python so I cannot determine if I can use a $2a hash in the Python code.

madcrazydrumma
  • 1,847
  • 3
  • 20
  • 38
  • Please post an example of a hashed + actual password. – lexicore Mar 04 '18 at 12:13
  • @lexicore updated OP – madcrazydrumma Mar 04 '18 at 12:22
  • Yes, seems like incompatible version. `$2a$...` would go. – lexicore Mar 04 '18 at 12:25
  • @lexicore Are you saying that `JBCrypt` requires `$2a$...` salt? Maybe there's a way in the Python module to use this – madcrazydrumma Mar 04 '18 at 12:29
  • I'm saying that `$2b$` is apparently not supported. It seems like these prefixes denote a version of hash. And from the quick googling looks like `$2a$` [was vulnerable](https://security.stackexchange.com/questions/20541/insecure-versions-of-crypt-hashes). Maybe jbcrypt was not updated yet? Last release is from 2015. – lexicore Mar 04 '18 at 12:37
  • Yeah that might be the issue, according to the `bcrypt` Python module v3.1.0 they `Fixed a regression where $2a hashes were vulnerable to a wraparound bug.` -- So there must be a way to use the `$2a` hash – madcrazydrumma Mar 04 '18 at 12:38
  • Yes, but that's on the Python side. – lexicore Mar 04 '18 at 12:39

2 Answers2

4

The problem was an issue with JBCrypt. It is outdated (it can verify only $2a hashes.

Therefore to fix this, I had to change the Python script to generate a salt which uses a $2a prefix:

Adjustable Prefix

Another one of bcrypt’s features is an adjustable prefix to let you define what libraries you’ll remain compatible with. To adjust this, pass either 2a or 2b (the default) to bcrypt.gensalt(prefix=b"2b") as a bytes object.

https://pypi.python.org/pypi/bcrypt/3.1.0

madcrazydrumma
  • 1,847
  • 3
  • 20
  • 38
2

From the source code, it seems the exception is coming from checking the format of the hashed password:

    if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
        throw new IllegalArgumentException ("Invalid salt version");
    if (salt.charAt(2) == '$')
        off = 3;
    else {
        minor = salt.charAt(2);
        if (minor != 'a' || salt.charAt(3) != '$')
            throw new IllegalArgumentException ("Invalid salt revision");
        off = 4;
    }

So my guess would be that dbHash for some reason does not match the format. Either it is generated by an incompatible version. Or it is actually not hashed. Or empty.

lexicore
  • 42,748
  • 17
  • 132
  • 221