1

我有一个使用 Oracle 11g 的基于 Java/Spring 的 Web 应用程序。目前,用户在登录时通过用户名/密码直接针对系统表进行身份验证SYS.USER$

这必须改变,所以我们创建了一个(常规)新表来存储所有用户数据。我们将所有现有密码插入到新创建的表中。但是,密码似乎以本网站描述的方式加密/散列

一个例子:一旦用户输入XXXXX,数据库存储07E4898C06DEF253.

我想使用存储在新(常规)表中的旧密码执行身份验证。我的问题是我不知道如何验证现有密码,因为我不知道它们是如何被散列/加密的。

我玩弄了ora_hashdbms_obfuscation_toolkit.DESDecrypt,但这些都没有给我一个正确的结果。我知道我的用户的正确密码,并且我可以看到 Oracle 为这个密码生成的值,但我无法重现 Oracle 通常“处理”密码数据的方式。

有没有办法在不重置所有密码的情况下解决这个问题?

4

1 回答 1

1

调整您在评论中链接到的 Java 实现,这很接近但没有正确使用盐:

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将是 的十六进制(OK,原始)等价物ZK3002,即5A4B33303032,与十六进制盐连接;所以5A4B333030321DB70EE23BFB23BDFD48

对于 Java 版本,您传递一个字节数组,但这意味着您需要将从存储的密码中提取的盐从十六进制转换回,然后再将其附加到密码上;并且由于它不太可能具有有用的字符串表示形式,因此您不妨将其直接放入字节数组中。因此,将密码转换为字节数组,将盐转换为字节数组,并将两个数组粘在一起。这将成为您传递给的值MessageDigest

您可以将生成的散列与 Oracle 散列版本进行比较,跳过初始S:盐和嵌入盐。

于 2012-08-08T08:37:52.767 回答