Does anybody know what encrypting technique is JDeveloper/SQL Developer using to persist credentials?

Viewed 31693

I'd be more than interesting for me to understand which technique is being used here to persist sensible data since I'm needing to implement a similar solution. Here's a sample connection configuration and the resulting exported snippet:

Oracle SQL Developer Connections

<?xml version = '1.0' encoding = 'UTF-8'?>
    <References xmlns="http://xmlns.oracle.com/adf/jndi">
        <Reference name="My Connection" className="oracle.jdeveloper.db.adapter.DatabaseProvider" xmlns="">
        <Factory className="oracle.jdeveloper.db.adapter.DatabaseProviderFactory"/>
        <RefAddresses>
            <StringRefAddr addrType="user">
                <Contents>username</Contents>
            </StringRefAddr>
            <StringRefAddr addrType="password">
                <Contents>054D4844D8549C0DB78EE1A98FE4E085B8A484D20A81F7DCF8</Contents>
            </StringRefAddr>
        <SKIPPED />
        </RefAddresses>
    </Reference>
</References>

Any advice would be really appreciated.

11 Answers

Note that Tim's password hash above is not for "apps_ro" - presumably he cut and pasted from the wrong place... I won't post the real password in case it's something he doesn't want shared!

I had a similar problem, trying to store my db credentials centrally (for non-secure databases!) and then exporting sql developer xml files. I have no idea what the algorithm is - however, you don't really need to know the algorithm, as you can just call the Oracle java API yourself. If you have SQLDeveloper, just grab the right Jar files:

cp /Applications/SQLDeveloper.App/Contents/Resources/sqldeveloper/BC4J/lib/db-ca.jar .
cp /Applications/SQLDeveloper.App/Contents/Resources/sqldeveloper/jlib/ojmisc.jar .

Then either load them in your Java app, or use something like JRuby as I do:

$jirb
> require 'java'
> require 'ojmisc.jar'
> require 'db-ca.jar'
> Java::oracle.jdevimpl.db.adapter.DatabaseProviderHelper.goingOut("password")    
 => "059D45F5EB78C99875F6F6E3C3F66F71352B0EB4668D7DEBF8" 
> Java::oracle.jdevimpl.db.adapter.DatabaseProviderHelper.goingOut("password")
 => "055CBB58B69B477714239157A1F95FDDD6E5B453BEB69E5D49" 
> Java::oracle.jdevimpl.db.adapter.DatabaseProviderHelper.comingIn("059D45F5EB78C99875F6F6E3C3F66F71352B0EB4668D7DEBF8")
 => "password" 
> Java::oracle.jdevimpl.db.adapter.DatabaseProviderHelper.comingIn("055CBB58B69B477714239157A1F95FDDD6E5B453BEB69E5D49")
 => "password" 

Note that the algorithm, whatever it is, has a random factor so the same password used twice can produce two different hex strings.

This solution works great for me... Copied from: http://www.mischiefblog.com/?p=912

import javax.crypto.*;
import javax.crypto.spec.*;
import java.security.*;

/**
 * Decrypt passwords stored in Oracle SQL Developer. This is intended for
 * password recovery.
 * 
 * Passwords are stored in
 * ~/.sqldeveloper/system2.1.1.64.39/o.jdeveloper.db.connection
 * .11.1.1.2.36.55.30/connections.xml
 */
public class Decrypt {
    public static byte[] decryptPassword(byte[] result)
            throws GeneralSecurityException {
        byte constant = result[0];
        if (constant != (byte) 5) {
            throw new IllegalArgumentException();
        }

        byte[] secretKey = new byte[8];
        System.arraycopy(result, 1, secretKey, 0, 8);

        byte[] encryptedPassword = new byte[result.length - 9];
        System.arraycopy(result, 9, encryptedPassword, 0,
                encryptedPassword.length);

        byte[] iv = new byte[8];
        for (int i = 0; i < iv.length; i++) {
            iv[i] = 0;
        }

        Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKey, "DES"),
                new IvParameterSpec(iv));
        return cipher.doFinal(encryptedPassword);
    }

    public static void main(String[] args) {
        if (args.length != 1) {
            System.err.println("Usage:  java Decrypt <password>");
            System.exit(1);
        }

        if (args[0].length() % 2 != 0) {
            System.err
                    .println("Password must consist of hex pairs.  Length is odd (not even).");
            System.exit(2);
        }

        byte[] secret = new byte[args[0].length() / 2];
        for (int i = 0; i < args[0].length(); i += 2) {
            String pair = args[0].substring(i, i + 2);
            secret[i / 2] = (byte) (Integer.parseInt(pair, 16));
        }

        try {
            System.out.println(new String(decryptPassword(secret)));
        } catch (GeneralSecurityException e) {
            e.printStackTrace();
            System.exit(3);
        }
    }
}

I'm not sure about this but I always thought hashes can't be decrypted, only compared to another hash. MD5 generates a hash. The saved password in SQL Developer needs to be decrypted and send to the server. So the DES3Encrypt and DES3Decrypt procedures in dbms_obfuscation_toolkit package are a better bet. But the decrypt should be called before connecting to a database, so it's probably a Java crypto package with DES methods.

I don't know, but I wouldn't be surprised if it was DBMS_OBFUSCATION_TOOLKIT being used something like this:

l_hash := dbms_obfuscation_toolkit.md5(input_string=>:username||:password);

The length of the hash is 50 hex characters, which is 200 bits, so it may be the the hash of the password with a salt, prepended with the salt, like:

salt | hash(salt | password)

where | means concatenation.

Just speculation though. My guess would be a 40-bit salt and a SHA-1 hash, since SHA-1 produces 160-bit hashes.

Would be helpful to provide some input/output test data to check against!

FYI the password 'apps_ro' encrypts as:

     <StringRefAddr addrType="password">
        <Contents>051DC8A88C574538CC4AEE32D326E9480659C06CEC271EA6D7</Contents>
     </StringRefAddr>
Related