我在申请中使用shiro进行身份验证。我使用散列密码和盐,我将它们存储在我的数据库中,如下所示:
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);
}
我在我的数据库中使用String存储salt。现在在我的领域我想从数据库中获取我的数据,我使用了一个transactionnal服务。但是我的salt是一个Strong,所以我希望它以静态方法转回ByteSource类型:
ByteSource byteSourceSalt = Util.bytes(salt); //where the salt is a String
但是当我创建我的SaltedAuthenticationInfo时,它不会被授权。
我认为我的问题来自我的转换方法:
private String byteArrayToHexString(byte[] bArray){
StringBuffer buffer = new StringBuffer();
for(byte b : bArray) {
buffer.append(Integer.toHexString(b));
buffer.append(" ");
}
return buffer.toString().toUpperCase();
}
感谢您的帮助。
答案 0 :(得分:13)
如优秀答案https://stackoverflow.com/a/20206115/603901中所述,Shiro的DefaultPasswordService已为每个密码生成唯一的salt。
但是,没有必要实现自定义PasswordService来向每个用户的salt添加私有盐(有时称为" pepper")。私有盐可以在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代码:
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");
结果哈希看起来像这样:
$shiro1$SHA-256$500000$An4HRyqMJlZ58utACtyGDQ==$nKbIY9Nd9vC89G4SjdnDfka49mZiesjWgDsO/4Ly4Qs=
私有盐未存储在数据库中,如果攻击者获得对数据库转储的访问权限,则会更难破解密码。
此示例是使用shiro-1.2.2
创建的感谢https://github.com/Multifarious/shiro-jdbi-realm/blob/master/src/test/resources/shiro.ini获取有关shiro.ini
语法的帮助答案 1 :(得分:4)
你看过PasswordMatcher / PasswordService吗?
这已经内置了所有编码/解码/比较逻辑。使用它:
在数据库中存储密码:
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);
}
然后你可以简单地使用PasswordMatcher作为你领域的匹配器。
realm.setCredentialsMatcher(new PasswordMatcher());
或在shiro.ini中:
matcher = org.apache.shiro.authc.credential.PasswordMatcher
realm.credentialsMatcher = $matcher
答案 2 :(得分:4)
DefaultPasswordService实现会自动为每个encryptPassword调用添加一个随机salt。该“公共”盐将存储在您从“encryptPassword”收到的“hashedPasswordBase64”中。
因为为每个散列密码单独生成“公共”盐,所以不能“简单地”生成彩虹表并立即强制所有散列密码。对于每个散列密码,攻击者必须生成一个独特的彩虹表,因为它具有独特的“公共”盐。到目前为止,您不需要在数据库中添加额外的盐。
为了使您存储的散列密码更加安全,您还可以添加应该存储在其他任何位置的“私有”盐 - 只要不在数据库中即可。通过使用“私有”盐,您可以保护散列密码免受暴力彩虹表攻击,因为攻击者不知道“私有”盐并且无法从数据库条目中获取“私有”盐。
这是一个非常基本的示例,如何创建一个PasswordService,它使用作为常量字符串提供的“私有”盐,并作为CredentialsMatcher使用:
public class MyPrivateSaltingPasswortService extends DefaultPasswordService
{
public MyPrivateSaltingPasswortService()
{
super();
HashService service = getHashService();
if (service instanceof DefaultHashService)
{
((DefaultHashService) service).setPrivateSalt(
new SimpleByteSource("MySuperSecretPrivateSalt"));
}
}
}
然后你可以在shiro.ini中使用你自己的实现:
[main]
saltedService = com.mycompany.MyPrivateSaltingPasswortService
matcher = org.apache.shiro.authc.credential.PasswordMatcher
matcher.passwordService = $saltedService
realm.credentialsMatcher = $matcher
此示例是使用shiro-1.2.2
创建的答案 3 :(得分:1)
为了省盐,我改变了我的类型。现在我使用byte []而不是String。
ByteSource salt = randomNumberGenerator.nextBytes(32);
byte[] byteTabSalt = salt.getBytes();
我在我的数据库中存储了byteTabSalt。