可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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.
回答1:
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
回答2:
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
回答3:
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
回答4:
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.