I am using Apache Shiro to create a web app user authentication capability. I have user login and home page JSPs adapted from the published Shiro tutorial examples. These are executed in Apache Tomcat. Shiro is configured with a JDBC Realm and the default authentication queries on user, role and permission tables in a prototype MySQL database that match the Shiro examples. Passwords are initially in clear text, and the database users table includes a salt column which is yet to be implemented. In this state, login and authentication using these components work fine and as expected.
I am now trying to implement hashed and salted passwords. My intention is that each user password has its own salt stored with it in the database. I am using a SHA-256 hash and storing the result in Base64 format. Here is the shiro.ini segment that attempts to define this configuration and in particular the credential matcher to perform the desired password hashing with salt from columns in the database:
# Configure the MySQL data source
ds = org.apache.commons.dbcp.BasicDataSource
ds.driverClassName = com.mysql.cj.jdbc.Driver
ds.username = ****
ds.password = ****
ds.url=jdbc:mysql://localhost:3306/shiro_users
# Use salted and hashed SHA-256 passwords stored in Base64 format
credentialsMatcher = org.apache.shiro.authc.credential.HashedCredentialsMatcher
credentialsMatcher.hashAlgorithmName = SHA-256
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashSalted = true
# Configure the JdbcRealm
jdbcRealm = org.apache.shiro.realm.jdbc.JdbcRealm
jdbcRealm.dataSource = $ds
jdbcRealm.credentialsMatcher = $credentialsMatcher
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.saltStyle = SaltStyle.COLUMN
jdbcRealm.authenticationQuery = SELECT password, salt from users WHERE username = ?
jdbcRealm.userRolesQuery = SELECT role_name FROM user_roles WHERE user_name = ?
jdbcRealm.permissionsQuery = SELECT permission FROM roles_permissions WHERE role_name = ?
securityManager.realms = $jdbcRealm
To populate the user database for development with hashed password and corresponding salt values, I created a prototype Java Password class. I adapted a set of methods from published examples to generate salt and hash clear text password values. Note that the hashed password and salt values are created and stored in the database in Base64 format. This is intended to be consistent with the shiro.ini setting of:
credentialsMatcher.storedCredentialsHexEncoded = false
For the present stage of development of the Password class, the hashed password and salt values are printed to the console for manual pasting into the appropriate database columns, replacing the clear text values that have otherwise worked fine.
Here are the key methods of the java Password class:
public class Password {
/**
* saveHashedPassword
*
* Generates salt and hashes the given clear text password. Both the hashed password
* and salt are encoded in Base64 and saved to the user database.
*
* Note: In this development version, the password and salt Base64 strings are
* printed to the console for manual pasting into the appropriate database columns.
*
* #param clearTextPassword The clear text password to be hashed and salted
*/
public void saveHashedPassword (String clearTextPassword) {
String hashedPassword;
byte[] saltBytes;
String salt;
saltBytes = getSalt();
hashedPassword = hashAndSaltPassword(clearTextPassword, saltBytes);
salt = Base64.getEncoder().encodeToString(saltBytes);
// persist salt and hash or return them to delegate this task to other component
System.out.println(hashedPassword.length() + "\tpwd \t" + hashedPassword);
System.out.println(salt.length() + "\tsalt\t" + salt);
}
/**
* hashAndSaltPassword
*
* Generates a hashed and salted Base64 password from a clear text password and
* salt byte array.
*
* #param clearTextPassword The clear text password to hash and salt
* #param salt The byte array salt value to use in hashing
* #return The hashed and salted password
*/
private String hashAndSaltPassword(String clearTextPassword, byte[] salt) {
byte[] hashedBytes;
String hashedPassword = "";
try {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(salt);
hashedBytes = digest.digest(clearTextPassword.getBytes(StandardCharsets.UTF_8));
hashedPassword = Base64.getEncoder().encodeToString(hashedBytes);
} catch (NoSuchAlgorithmException e) {
System.out.println(e.getMessage());
}
return hashedPassword;
}
/**
* getSalt
*
* Generates a salt byte array
*
* #return A byte array salt value
*/
private byte[] getSalt() {
SecureRandom random = new SecureRandom();
byte[] salt = new byte[16];
random.nextBytes(salt);
return salt;
}
}
For a clear text password of “secret”, Password,hashAndSaltPassword() returns the following values (printed to the console and manually pasted into the appropriate database columns):
Password: mKYfzHXt4kHoLgzc7/C2upKXuCZ0SSGjUrnGIqzd92E=
Salt: bigG9fGM9mjcqNiprFQ4+g==
When the web app executes with the hashed password Shiro.ini configuration and the hashed password and salt values above in the database, the login process fails. (It worked fine before applying the password hash.) No exceptions are thrown and neither Shiro nor Tomcat gives any diagnostic other than the login fails. No errors are reported in the normal app logs.
It appears that the result of Shiro's hash of the clear text password at run time and the stored hashed password and salt in the database produced by the Password.hashAndSaltPassword() method do not match. I think there is something inconsistent between the configuration of the Shiro credential matcher and the means of creating the stored hashed and salted password, but I have been unable to diagnose the problem further or find a specific example that illuminates the use case completely.
Any help diagnosing the problem, referral to appropriate resources or other constructive observations or suggestions are much appreciated. Many thanks.
Related
Some of developers need to move storage from parse.com to another servers.
When I exported my data from parse, I get json data. This json data has encrypted passwords (bcrypt) like:
$2a$10$pcR4SaZd3PMD/nXQKMssxupMLncDoFwfU7avg/wdpLVChNqGOXbLu
I try to understand, how to check password from user in this case.
I using jBcrypt like this:
import org.mindrot.jbcrypt.BCrypt;
public class Main {
public static void main(String[] args) {
String candidate = "$2a$10$pcR4SaZd3PMD/nXQKMssxupMLncDoFwfU7avg/wdpLVChNqGOXbLu";
String password = "123";
String hashed = BCrypt.hashpw(password, BCrypt.gensalt());
if (BCrypt.checkpw(candidate, hashed)) {
System.out.println("It matches");
}
else {
System.out.println("It does not match");
}
}
}
In this case passwords don't much. But if we go to https://www.dailycred.com/article/bcrypt-calculator
and try to use BCrypt Tester with hashed, candidate strings and "123" password it's all ok.
How can I understand do user's password match with bcrypt string or not?
BCrypt.checkpw() takes a plain text password as it's first parameter, and will then hash it and compare it to the second parameter (docs); in your case you're giving it an already hashed password as it's first parameter, which it will then hash again hence it not matching.
I use Spring security in this question but its knowledge might not be required to answer this question.
I am trying to understand how Spring security's blowfish encryption class (BCrypt) verifies a given password. I tried to :
Generate a Salt with BCrypt.gensalt(10);
Encode a password using this salt with BCrypt.hashpw(clearText, salt).
It worked well.Then, I wanted to store the used salt in the database in order to be able to reuse it when the users enter a password.
I realized that BCryptPasswordEncoder does not use the salt when it checks the password. Instead, it crypts the password with its hashed password as salt :
public boolean matches(CharSequence rawPassword, String encodedPassword) {
[...]
return BCrypt.checkpw(rawPassword.toString(), encodedPassword);
}
So I tried this piece of code :
public static void main(String[] args) {
String salt = BCrypt.gensalt(12);
String clearText="test";
String hashed = BCrypt.hashpw(clearText, salt);
String reHashed = BCrypt.hashpw(clearText, hashed);
System.out.println("salt : " + salt);
System.out.println(hashed);
System.out.println(reHashed);
}
output :
salt : $2a$12$gzUymsNBoW.f1OfkLpb2se
$2a$12$gzUymsNBoW.f1OfkLpb2seFZrniorawujSOp6Qe.PWDIHJvmYSP6y
$2a$12$gzUymsNBoW.f1OfkLpb2seFZrniorawujSOp6Qe.PWDIHJvmYSP6y
This seems to confirm that :
good password => hashed = hash(clearPassword, hashed)
Am I understanding it wrong? Does it mean that the salt is useless for password checking? Hence, does it mean that I do not need to store the salt in database?
BCrypt stores the salt as part of the "hash" string that it returns.
This is handled by the Bcrypt.* functions, so you don't need to do anything.
I'm a beginner with Apache Shiro. I've been following the docs and lots of other tutorials, blogs etc. but I just can't get the authentication to work. When I attempt to login with a valid username and password, I always get an InvalidCredentialsException thrown. I'm using DynamoDB as a custom realm for storing user credentials, but I really don't think that matters. It's obviously the way that I'm storing and/or doing the credential matching that's not correct. Here's my setup:
Shiro.ini:
[main]
myRealm = com.enki.closing.users.DynamoDBRealm
credentialsMatcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
credentialsMatcher.storedCredentialsHexEncoded = false
credentialsMatcher.hashIterations = 1024
myRealm.credentialsMatcher = $credentialsMatcher
Create user account:
String password = ...
ByteSource passwordSalt = new SecureRandomNumberGenerator().nextBytes();
String hashedPasswordBase64 = new Sha256Hash(password, passwordSalt, 1024).toBase64();
// store the hashedPassword and salt in DynamoDB...
// I've tried storing the salt with and without base64 encoding.
The password and salt are stored fine in DynamoDB, the values look alright. Here's the custom realm for authentication:
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken userPass = (UsernamePasswordToken) token;
String username = userPass.getUsername();
...
// pull the matching password and salt out of DynamoDB, no problems...
ByteSource passwordSalt = ByteSource.Util.bytes( storedPasswordSalt );
return new SimpleAuthenticationInfo(username, passwordHash, passwordSalt, getName());
}
This is all pretty much what the docs are telling me to do, but there's something not right. When I try the login, it get InvalidCredentialsException.
I figured out how to get it working. I had to change this (in my custom realm impl):
ByteSource passwordSalt = ByteSource.Util.bytes( storedPasswordSalt );
to this:
ByteSource passwordSalt = ByteSource.Util.bytes(
Base64.decode( storedPasswordSalt) );
How do I encrypt a password insert it into the db and after the comparison when he will want to connect?
I would use StandardPasswordEncoder Spring security 3.1.4 to encrypt my password and insert into the db. But how do I recovered the salt generated by the method?
Here is an example of the doc Spring security:
StandardPasswordEncoder encoder = new StandardPasswordEncoder("secret");
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));
I asked her because I'll need the selt order to re encode the password for the comparison? And validate if the user has to enter the correct password?
Here the password encoding: 9e7e3a73a40871d4b489adb746c31ace280d28206dded9665bac40eabfe6ffdc32a8c5c416b5878f
and I would compare encode the new password
Link Doc Spring : http://docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/reference/crypto.html
Link API SPring security 3.1.4 : http://docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/apidocs/
I think you are asking how it works?? The answer is fairly simple. StandardPasswordEncoder.matches() is the method you want to use. Behind the scenes, StandardPasswordEncoder will decode the hashed password and extract the salt from the resulting byte array. It will then use that salt to hash the plain-text password you passed in. If the resulting hash matches the original hash, your passwords match! Refer to the source for the details behind StandardPasswordEncoder.matches():
public boolean matches(CharSequence rawPassword, String encodedPassword) {
byte[] digested = decode(encodedPassword);
byte[] salt = subArray(digested, 0, saltGenerator.getKeyLength());
return matches(digested, digest(rawPassword, salt));
}
You cant decrepit the saved password as human readable.
assume myPassword ="9e7e3a73a40871d4b489adb746c31ace280d28206dded9665bac40eabfe6ffdc32a8c5c416b5878f" pesent in the daabase.
You can do like this
StandardPasswordEncoder encoder = new StandardPasswordEncoder("secret");
String result = encoder.encode("myPassword");
now your result is equal to `9e7e3a73a40871d4b489adb746c31ace280d28206dded9665bac40eabfe6ffdc32a8c5c416b5878f`
String passworddb = getPasswordFromDB();
passworddb from daabase is `9e7e3a73a40871d4b489adb746c31ace280d28206dded9665bac40eabfe6ffdc32a8c5c416b5878f`
assertTrue(encoder.matches(passworddb, result)); then passworddb and result are equal.
I use shiro in application for the authenticate. I use hashed password with a salt and I store them in my database like this :
private User createUserWithHashedPassword(String inName, String inFirstName, String inLastName, String inPassword){
ByteSource salt = randomNumberGenerator.nextBytes(32);
byte[] byteTabSalt = salt.getBytes();
String strSalt = byteArrayToHexString(byteTabSalt);
String hashedPasswordBase64 = new Sha256Hash(inPassword, salt, 1024).toBase64();
return new User(inName,inFirstName,inLastName,hashedPasswordBase64,strSalt);
}
I store the salt with a String in my database. Now in my realm I want to get back my datas from the database, I use a transactionnal service for this. But my salt is a Strong so I want it to turn back as ByteSource type with the static method :
ByteSource byteSourceSalt = Util.bytes(salt); //where the salt is a String
But when I create my SaltedAuthenticationInfo it doesn't auth.
I think my problem is from my convert method :
private String byteArrayToHexString(byte[] bArray){
StringBuffer buffer = new StringBuffer();
for(byte b : bArray) {
buffer.append(Integer.toHexString(b));
buffer.append(" ");
}
return buffer.toString().toUpperCase();
}
Thanks for your help.
As mentioned in the excellent answer https://stackoverflow.com/a/20206115/603901, Shiro's DefaultPasswordService already generates unique salts for each password.
However, there is no need to implement a custom PasswordService to add a private salt (sometimes called "pepper") to the per-user salts. Private salt can be configured in shiro.ini:
[main]
hashService = org.apache.shiro.crypto.hash.DefaultHashService
hashService.hashIterations = 500000
hashService.hashAlgorithmName = SHA-256
hashService.generatePublicSalt = true
# privateSalt needs to be base64-encoded in shiro.ini but not in the Java code
hashService.privateSalt = myVERYSECRETBase64EncodedSalt
passwordMatcher = org.apache.shiro.authc.credential.PasswordMatcher
passwordService = org.apache.shiro.authc.credential.DefaultPasswordService
passwordService.hashService = $hashService
passwordMatcher.passwordService = $passwordService
Java code for generating a matching password hash:
DefaultHashService hashService = new DefaultHashService();
hashService.setHashIterations(HASH_ITERATIONS); // 500000
hashService.setHashAlgorithmName(Sha256Hash.ALGORITHM_NAME);
hashService.setPrivateSalt(new SimpleByteSource(PRIVATE_SALT)); // Same salt as in shiro.ini, but NOT base64-encoded.
hashService.setGeneratePublicSalt(true);
DefaultPasswordService passwordService = new DefaultPasswordService();
passwordService.setHashService(hashService);
String encryptedPassword = passwordService.encryptPassword("PasswordForThisUser");
The resulting hash looks like this:
$shiro1$SHA-256$500000$An4HRyqMJlZ58utACtyGDQ==$nKbIY9Nd9vC89G4SjdnDfka49mZiesjWgDsO/4Ly4Qs=
The private salt is not stored in the database, which makes it harder to crack the passwords if an adversary gains access to a database dump.
This example was created using shiro-1.2.2
Thanks to https://github.com/Multifarious/shiro-jdbi-realm/blob/master/src/test/resources/shiro.ini for help with the syntax for shiro.ini
Have you looked at PasswordMatcher / PasswordService?
This already has all of the encoding/decoding/compare logic built-in. To use it:
Storing password in database:
PasswordService service = new DefaultPasswordService(); // or use injection or shiro.ini to populate this
private User createUserWithHashedPassword(String inName, String inFirstName, String inLastName, String inPassword){
String hashedPasswordBase64 = service.encryptPassword(inPassword);
return new User(inName,inFirstName,inLastName,hashedPasswordBase64,strSalt);
}
Then you can simply use PasswordMatcher as the matcher in your realm.
realm.setCredentialsMatcher(new PasswordMatcher());
or in shiro.ini:
matcher = org.apache.shiro.authc.credential.PasswordMatcher
realm.credentialsMatcher = $matcher
The DefaultPasswordService implementation automatically adds a random salt to each encryptPassword call. That "public" salt will be stored within the "hashedPasswordBase64" that you receive from "encryptPassword".
Because the "public" salt is individually generated for each hashed password one cannot "simply" generate a rainbow table and brute-force all your hashed passwords at once. For each hashed password the attacker would have to generate an own, unique rainbow table because of the unique "public" salt. So far you do not need to put an extra salt into the database.
To make your stored hashed passwords even more secure you can furthermore add a "private" salt that should be stored anywhere else - as long as not in the database. By using a "private" salt you could protect the hashed passwords against a brute-force rainbow-table attack, because the attacker does not know the "private" salt and cannot gain the "private" salt from the database entries.
This is a very basic example how to create a PasswordService that utilizes a "private" salt provided as a constant string and that works as CredentialsMatcher:
public class MyPrivateSaltingPasswortService extends DefaultPasswordService
{
public MyPrivateSaltingPasswortService()
{
super();
HashService service = getHashService();
if (service instanceof DefaultHashService)
{
((DefaultHashService) service).setPrivateSalt(
new SimpleByteSource("MySuperSecretPrivateSalt"));
}
}
}
you then could use your own implementation in shiro.ini:
[main]
saltedService = com.mycompany.MyPrivateSaltingPasswortService
matcher = org.apache.shiro.authc.credential.PasswordMatcher
matcher.passwordService = $saltedService
realm.credentialsMatcher = $matcher
This example was created using shiro-1.2.2
I change my type for the save of my salt. Now I'm using a byte[] instead of a String.
ByteSource salt = randomNumberGenerator.nextBytes(32);
byte[] byteTabSalt = salt.getBytes();
And I stock the byteTabSalt in my database.