我有一个基于Java / Spring的Web应用程序,它使用Oracle 11g。目前,用户在登录时直接通过用户名/密码对系统表SYS.USER$
进行身份验证。
这必须改变,所以我们创建了一个(常规)新表来存储那里的所有用户数据。我们将所有现有密码插入到新创建的表中。但是,密码似乎是以this site
描述的方式加密/散列一个例子:用户输入XXXXX
后,数据库会存储07E4898C06DEF253
。
我想使用存储在新(常规)表中的旧密码执行身份验证。我的问题是我不知道如何验证现有密码,因为我不确切知道它们是如何被散列/加密的。
我玩ora_hash和dbms_obfuscation_toolkit.DESDecrypt,但这些都没有给我一个正确的结果。我知道我的用户的正确密码,我可以看到Oracle为此创建的值,但我无法重现Oracle通常“处理”密码数据的方式。
有没有办法在不重置所有密码的情况下解决这个问题?
答案 0 :(得分:1)
在注释中调整您链接到的Java实现,该注释很接近但是没有正确使用salt:
import java.security.MessageDigest;
import java.util.Formatter;
class Main{
public static String calculateHash(String password) throws Exception{
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
String encodedPassword = "S:71752CE0530476A8B2E0DD218AE59CB71B211D7E1DB70EE23BFB23BDFD48";
// Convert password to bytes
byte[] bPassword = password.getBytes("UTF-8");
// Get salt from encoded password
String salt = encodedPassword.substring(42, 62);
System.out.println("Salt is " + salt);
// Convert salt from hex back to bytes
// based on http://stackoverflow.com/a/140861/266304
int len = salt.length();
byte[] bSalt = new byte[len / 2];
for (int i = 0; i < len; i += 2) {
bSalt[i / 2] = (byte) ((Character.digit(salt.charAt(i), 16) << 4)
+ Character.digit(salt.charAt(i+1), 16));
}
// Add converted salt to password bytes
// based on http://stackoverflow.com/a/80503/266304
byte[] bData = new byte[bPassword.length + bSalt.length];
System.arraycopy(bPassword, 0, bData, 0, bPassword.length);
System.arraycopy(bSalt, 0, bData, bPassword.length, bSalt.length);
// Hash the final byte array
crypt.update(bData);
byte bHash[] = crypt.digest();
Formatter formatter = new Formatter();
for (byte b : bHash)
{
formatter.format("%02x", b);
}
System.out.println("Expected " + encodedPassword.substring(2,42));
return formatter.toString().toUpperCase();
}
public static void main(String[] args) throws Exception {
System.out.println("The result is " + calculateHash("ZK3002"));
}
}
提供输出:
Salt is 1DB70EE23BFB23BDFD48
Expected 71752CE0530476A8B2E0DD218AE59CB71B211D7E
The result is 71752CE0530476A8B2E0DD218AE59CB71B211D7E
PL / SQL版本涉及一些转换; dbms_crypto.hash()
采用RAW
参数,因此您必须将纯文本密码转换为RAW
,然后连接提取的盐 - 已经是十六进制。 (在Pete Finnigan博客的PL / SQL版本中,你可能会注意到他有一个明确的hextoraw
调用,所以我简化了一点)。因此,对于您的示例,传递给dbms_crypto.hash
的参数将是ZK3002
的十六进制(OK,raw)等价,其为5A4B33303032
,其中六边形盐与其连接;所以5A4B333030321DB70EE23BFB23BDFD48
。
对于Java版本,您传递一个字节数组,但这意味着您需要将从存储密码中提取的salt转换回十六进制,然后再将其添加到密码中;并且因为它不太可能有一个有用的字符串表示,所以你可以将它直接放入字节数组中。因此,将密码转换为字节数组,将salt转换为字节数组,并将两个数组放在一起。然后,这将成为您传递给MessageDigest
的值。
您可以将此产生的哈希与Oracle哈希版本进行比较,跳过初始S:
和嵌入式盐。