8

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.

Fred37b
  • 822
  • 2
  • 10
  • 29

4 Answers4

13

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

Community
  • 1
  • 1
Mikael Falkvidd
  • 413
  • 6
  • 13
  • 2
    how can I get public salt auto generated by shiro in order to store it to seperated column in db – thoitbk Mar 04 '15 at 15:20
4

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
jbunting
  • 886
  • 5
  • 10
  • 4
    Ok, thanks for your answer. I will try this. But I don't understand where I have to use my salt now and where is the link between the password and the salt. – Fred37b Sep 20 '12 at 08:16
4

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

seafoxx
  • 1,731
  • 1
  • 13
  • 17
  • Could you explain:"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"."? I don't understand must this public salt be saved in separate column (not password column)? –  Nov 08 '14 at 20:15
  • So far you do not need to put an extra salt into the database. - What did you mean here? –  Nov 08 '14 at 20:45
  • 1
    Hello Pasha, the generated token contains several different information blocks. one of these blocks is the public salt so Shiro can re-hash and test an user-entered password again. as long as you store the generated token "as is" you do not need to store the public salt - you do not even to know it because Shiro takes care of it. Please take a look at the answer from Mikael Falkvidd - there is an example of a generated token and also an option to define a private salt. – seafoxx Nov 09 '14 at 21:14
1

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.

Fred37b
  • 822
  • 2
  • 10
  • 29