1

I have a web application based on Java/Spring that uses Oracle 11g. Currently, the users authenticate via username/password directly against the system table SYS.USER$ on login.

This has to change, so we created a (regular) new table to store all the user data there. We inserted all existing passwords to the newly created table. However, the passwords seem to be encrypted/hashed in a way that's described by this site

One example: Once the user enters XXXXX, the database stores 07E4898C06DEF253.

I want to perform the authentication with the old passwords stored in the new (regular) table. My problem is that I don't know how to verify the existing passwords since I don't know exactly how they have been hashed/encrypted.

I played around with ora_hash and dbms_obfuscation_toolkit.DESDecrypt, but none of these gave me a correct result. I know the correct password for my user and I can see Oracle's generated value for this one, but I can't reproduce the way Oracle generally "handles" the password data.

Is there any way to solve this problem without resetting all passwords?

Ben
  • 51,770
  • 36
  • 127
  • 149
Jan Petzold
  • 1,561
  • 1
  • 15
  • 24
  • 1
    If you know all the clear-text passwords, why don't you use your own salting+hashing procedure, and replace the old hashed passwords by the new hashed ones? – JB Nizet Aug 07 '12 at 11:34
  • No, I don't know all the passwords, just the one for my (admin) user. I'll modify the original question to make that more clear. – Jan Petzold Aug 07 '12 at 12:00
  • Ah, OK. The linked page says that Oracle11g stores SHA1(password || salt) in a column. It doesn't know what the salt is or where it's stored though. If you can find it, implementing this same algorithm should be very easy. See http://www.petefinnigan.com/weblog/archives/00001097.htm for details. – JB Nizet Aug 07 '12 at 12:05
  • Thanks for the link, tried that already. Unfortunately I don't have the rights to access SYS.DBMS_CRYPTO. Also, I read on another page that Oracle is using a different salt every time. Actually I don't really need to decode the password, I just want to compare the entered password to the stored one, so there must be some Oracle functionality enabling me to to that... – Jan Petzold Aug 07 '12 at 12:14
  • SHA1 is a standard algorithm, available in every language on earth. You could hash the password+salt in Java, and compare the result with what is stored in the database. SHA-1 is a one-way function, so reverting the clear-text password from the hash is impossible. The salt seems to be stored in the column, after SHA1(password + salt). – JB Nizet Aug 07 '12 at 12:19
  • The example you gave ("07E4898C06DEF253") looks like the value in `password` column, don't you need the value in `spare4` column ? you are using the 11g. Check if `sec_case_sensitive_logon=true` – A.B.Cade Aug 07 '12 at 12:30
  • Oracle does use an different salt each time but that's stored in `user$` as well so you just need to put this value in your own users table. It seems like the simplest solution would be to get access to `dbms_crypto`. What's stopping you from doing so? If it's a DBA forbidding you access they should also have a reason for doing so. – Ben Aug 07 '12 at 12:49
  • Yes, you are right - this is the value in PASSWORD. In SPARE4, I have a SHA-1 encoded value. So I have both and it doesn't really matter which one I use for the validation. Currently I'm fiddling around with the SHA-1 encoding since this looks like the best way to go, but I'm not done yet... – Jan Petzold Aug 07 '12 at 12:50
  • By the way this is the resource that mentioned that the SHA-1 salt by oracle is random: [http://marcel.vandewaters.nl/oracle/security/password-hashes](http://marcel.vandewaters.nl/oracle/security/password-hashes) – Jan Petzold Aug 07 '12 at 12:53
  • @Ben: Where is the salt stored in `user$` ? – Jan Petzold Aug 07 '12 at 13:15
  • 1
    The article linked to by JB Nizet has it, `substr(spare4,43,20)` from `user$` – Ben Aug 07 '12 at 13:48
  • I did a Java implementation, but it still gives me a wrong result. Maybe you can have a look: [http://ideone.com/DBCGU](http://ideone.com/DBCGU) The SHA-1 encoded result is there, password is "ZK3002". – Jan Petzold Aug 07 '12 at 15:28

1 Answers1

1

Adapting the Java implementation you linked to in a comment, which is close but isn't quite using the salt properly:

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"));
    }
}

Which gives output:

Salt is 1DB70EE23BFB23BDFD48
Expected      71752CE0530476A8B2E0DD218AE59CB71B211D7E
The result is 71752CE0530476A8B2E0DD218AE59CB71B211D7E

The PL/SQL version involves some conversion; dbms_crypto.hash() takes a RAW argument, so you have to convert the plain-text password to RAW, then concatenate the extracted salt - which is already hex. (In the PL/SQL version in Pete Finnigan's blog you may notice that he has an explicit hextoraw call, so I'm simplifying a bit). So the argument passed to dbms_crypto.hash for your example would be the hex (OK, raw) equivalent of ZK3002, which is 5A4B33303032, with the hex salt concatenated to that; so 5A4B333030321DB70EE23BFB23BDFD48.

For the Java version you pass a byte array, but that means you need to convert the salt extracted from the stored password back from hex before tacking it on to the password; and since it's unlikely to have a useful string representation you might as well put it straight into a byte array. So, convert the password to a byte array, convert the salt into a byte array, and stick the two arrays together. That then becomes the value you pass to MessageDigest.

You can compare the hash this produces with the Oracle-hashed version, skipping the initial S: and the embedded salt.

Alex Poole
  • 183,384
  • 11
  • 179
  • 318
  • So there is no other way than brute force to find his passwords. Not surprised. – Matin Kh Aug 08 '12 at 10:42
  • @MatinKh - I don't think he particularly wanted to un-hash them (which is of course not possible, without brute-forcing them), just wanted to authenticate against the Oracle-hashed value. I'd probably consider only doing this the first time a user logged in, then re-hash the password in a non-Oracle way (maybe` bcrypt`, but whatever) and store that instead for future use. Depends why it has to change in the first place maybe. – Alex Poole Aug 08 '12 at 10:54
  • Yeah I get it now. From the question I got this feeling that he is looking for a way for decrypting those encrypted passwords. (which would surprise me if anyone gave him the answer) – Matin Kh Aug 08 '12 at 11:05
  • OK, tried that now - works perfectly. I also thought that the problem might relate to the hex/byte conversion but couldn't figure out how to build that in Java. Thanks a lot! – Jan Petzold Aug 08 '12 at 14:48