2012-08-07 101 views
1

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

这必须改变,所以我们创建了一个(常规)新表来存储所有用户数据。我们将所有现有的密码插入到新创建的表中。然而,密码似乎要加密/散列在于通过真实this site

一个实例描述的方式:一旦用户输入XXXXX,所述数据库存储07E4898C06DEF253

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

我玩过ora_hashdbms_obfuscation_toolkit.DESDecrypt,但没有一个给我一个正确的结果。我知道我的用户的密码是正确的,我可以看到Oracle为此产生的值,但我无法再现Oracle通常“处理”密码数据的方式。

有没有办法解决这个问题,而无需重置所有密码?

+1

如果您知道所有明文密码,为什么不使用自己的salting + hashing过程,并用新的哈希密码替换旧的密码? – 2012-08-07 11:34:53

+0

不,我不知道所有的密码,只是我的(管理员)用户的密码。我会修改原来的问题,使其更清楚。 – 2012-08-07 12:00:35

+0

啊,好的。链接页面表示Oracle11g将SHA1(密码|| salt)存储在一列中。它不知道盐是什么或它存储在哪里。如果你能找到它,那么实现这个相同的算法应该很容易。有关详细信息,请参阅http://www.petefinnigan.com/weblog/archives/00001097.htm。 – 2012-08-07 12:05:28

回答

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,然后连接已提取的salt(已经是十六进制)。 (在Pete Finnigan博客的PL/SQL版本中,您可能会注意到他有一个明确的hextoraw调用,所以我简化了一下)。因此,对于您的示例,传递给dbms_crypto.hash的参数应该是ZK3002的十六进制(正常,未加工)等效,即5A4B33303032,其中十六进制的盐级联到该值;所以5A4B333030321DB70EE23BFB23BDFD48

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

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

+0

所以没有其他方法比蛮力找到他的密码。不感到惊讶。 – 2012-08-08 10:42:24

+0

@MatinKh - 我不认为他特别想要对它们进行散列(当然这是不可能的,没有暴力强制它们),只是想通过Oracle散列值进行验证。我可能会考虑只在用户第一次登录时执行此操作,然后以非Oracle方式(也许是'bcrypt',但不管)来重新密码并将其存储以备将来使用。取决于为什么它可能会首先改变。 – 2012-08-08 10:54:47

+0

是的,我现在明白了。从这个问题中我得到了这种感觉,他正在寻找解密这些加密密码的方法。 (如果有人给了他答案,这会让我感到惊讶) – 2012-08-08 11:05:01