Keycloak 自定义 PasswordHashProvider,每个连接上的盐更改
Keycloak custom PasswordHashProvider, salt change on each connection
我正在尝试创建自定义 PasswordHashProvider。
组件创建成功,算法正常,数据库保存的密码正确
Salt 存储在数据库中,但我不明白如何。我以为是 base 64 盐,但是不匹配。
database secret_data : {"value":"v8Po+hMGZbS2tWr4IGRXqy3FJyQIavjsrpXHwomWHwRRs+iRVInd+9naF681EENm76lF1omK+BqsPIRrtKH1VQ==","salt":"DEiO17h4/+6gwzxn"}
database credential_data : {"hashIterations":5000,"algorithm":"message-digest"}
我在创建密码和连接用户时在控制台中添加了日志。生成、存储盐,但每次尝试连接期间检索的盐都不同。
当我注册用户密码时:
New salt : [B@7bb894a0
当我尝试连接用户时:
Existing salt: [B@d433036
// retry connection
Existing salt: [B@355bae9a
有谁知道盐是怎么储存的?
我的 class(对于测试,我强制迭代到 5000):
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.credential.PasswordCredentialModel;
import java.util.Base64;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
public class MessageDigestPasswordHashProvider implements PasswordHashProvider {
private final int defaultIterations;
private final String providerId;
private byte[] salt;
public MessageDigestPasswordHashProvider(String providerId, int defaultIterations) {
this.providerId = providerId;
this.defaultIterations = defaultIterations;
}
@Override
public boolean policyCheck(PasswordPolicy policy, PasswordCredentialModel credential) {
int policyHashIterations = 5000;
return credential.getPasswordCredentialData().getHashIterations() == policyHashIterations
&& providerId.equals(credential.getPasswordCredentialData().getAlgorithm());
}
@Override
public PasswordCredentialModel encodedCredential(String rawPassword, int iterations) {
iterations = 5000;
byte[] salt = generateSalt();
this.salt = salt;
System.out.print("New salt : ");
System.out.println(salt);
String encodedPassword = encode(rawPassword, iterations);
return PasswordCredentialModel.createFromValues(providerId, salt, iterations, encodedPassword);
}
@Override
public String encode(String rawPassword, int iterations) {
iterations = 5000;
byte[] salt = this.salt;
System.out.print("Salt fonction encode :");
System.out.println(salt);
String salted = rawPassword + "{" + salt.toString() + "}";
byte[] digest = getSha512(salted.getBytes());
System.out.println(iterations);
for (int i = 1; i < iterations; i++) {
digest = getSha512(concat(digest, salted.getBytes()));
}
return Base64.getEncoder().encodeToString(digest);
}
@Override
public boolean verify(String rawPassword, PasswordCredentialModel credential) {
this.salt = credential.getSalt();
System.out.print("Existing salt: ");
System.out.println(this.salt);
String encodedPassword = encode(rawPassword, credential.getHashIterations());
return encodedPassword.equals(credential.getValue());
}
private static byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte salt[] = new byte[12];
random.nextBytes(salt);
return salt;
}
@Override
public void close() {
}
public static byte[] getSha512(byte[] value) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] messageDigest = md.digest(value);
return messageDigest;
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static byte[] concat(byte[] a, byte[] b) {
byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
}
A Salt is stored in a database, but I don't understand how. I thought
it was the base 64 salt, but it doesn't match.
它基于 64。
The salt is generated, stored, but the one retrieved during connection
is different on each try.
问题是这样的:
System.out.print("New salt : ");
System.out.println(salt);
您打印的不是盐的实际含量,而是打印对象“盐”。
试试看:
System.out.println(Arrays.toString(this.salt));
我正在尝试创建自定义 PasswordHashProvider。 组件创建成功,算法正常,数据库保存的密码正确
Salt 存储在数据库中,但我不明白如何。我以为是 base 64 盐,但是不匹配。
database secret_data : {"value":"v8Po+hMGZbS2tWr4IGRXqy3FJyQIavjsrpXHwomWHwRRs+iRVInd+9naF681EENm76lF1omK+BqsPIRrtKH1VQ==","salt":"DEiO17h4/+6gwzxn"}
database credential_data : {"hashIterations":5000,"algorithm":"message-digest"}
我在创建密码和连接用户时在控制台中添加了日志。生成、存储盐,但每次尝试连接期间检索的盐都不同。
当我注册用户密码时:
New salt : [B@7bb894a0
当我尝试连接用户时:
Existing salt: [B@d433036
// retry connection
Existing salt: [B@355bae9a
有谁知道盐是怎么储存的?
我的 class(对于测试,我强制迭代到 5000):
import org.keycloak.credential.hash.PasswordHashProvider;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.credential.PasswordCredentialModel;
import java.util.Base64;
import java.security.MessageDigest;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;
import java.math.BigInteger;
import java.security.NoSuchAlgorithmException;
public class MessageDigestPasswordHashProvider implements PasswordHashProvider {
private final int defaultIterations;
private final String providerId;
private byte[] salt;
public MessageDigestPasswordHashProvider(String providerId, int defaultIterations) {
this.providerId = providerId;
this.defaultIterations = defaultIterations;
}
@Override
public boolean policyCheck(PasswordPolicy policy, PasswordCredentialModel credential) {
int policyHashIterations = 5000;
return credential.getPasswordCredentialData().getHashIterations() == policyHashIterations
&& providerId.equals(credential.getPasswordCredentialData().getAlgorithm());
}
@Override
public PasswordCredentialModel encodedCredential(String rawPassword, int iterations) {
iterations = 5000;
byte[] salt = generateSalt();
this.salt = salt;
System.out.print("New salt : ");
System.out.println(salt);
String encodedPassword = encode(rawPassword, iterations);
return PasswordCredentialModel.createFromValues(providerId, salt, iterations, encodedPassword);
}
@Override
public String encode(String rawPassword, int iterations) {
iterations = 5000;
byte[] salt = this.salt;
System.out.print("Salt fonction encode :");
System.out.println(salt);
String salted = rawPassword + "{" + salt.toString() + "}";
byte[] digest = getSha512(salted.getBytes());
System.out.println(iterations);
for (int i = 1; i < iterations; i++) {
digest = getSha512(concat(digest, salted.getBytes()));
}
return Base64.getEncoder().encodeToString(digest);
}
@Override
public boolean verify(String rawPassword, PasswordCredentialModel credential) {
this.salt = credential.getSalt();
System.out.print("Existing salt: ");
System.out.println(this.salt);
String encodedPassword = encode(rawPassword, credential.getHashIterations());
return encodedPassword.equals(credential.getValue());
}
private static byte[] generateSalt() {
SecureRandom random = new SecureRandom();
byte salt[] = new byte[12];
random.nextBytes(salt);
return salt;
}
@Override
public void close() {
}
public static byte[] getSha512(byte[] value) {
try {
MessageDigest md = MessageDigest.getInstance("SHA-512");
byte[] messageDigest = md.digest(value);
return messageDigest;
}
catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
public static byte[] concat(byte[] a, byte[] b) {
byte[] c = new byte[a.length + b.length];
System.arraycopy(a, 0, c, 0, a.length);
System.arraycopy(b, 0, c, a.length, b.length);
return c;
}
}
A Salt is stored in a database, but I don't understand how. I thought it was the base 64 salt, but it doesn't match.
它基于 64。
The salt is generated, stored, but the one retrieved during connection is different on each try.
问题是这样的:
System.out.print("New salt : ");
System.out.println(salt);
您打印的不是盐的实际含量,而是打印对象“盐”。
试试看:
System.out.println(Arrays.toString(this.salt));