Hi all,
I'm trying to simulate Oracle's password Hashing in Java., because I want to change the authentification-mechanism of an application (currently via Oracle) to an application-build-in authentification.
I used this website as guideline: http://www.sans.org/reading_room/special/?id=oracle_pass&ref=911
But the output is not what I exspected. What's wrong? Any idea?
Expected: 342C0CF1DDCAD9F3
Got:
9gQc62TlB/zI3pN6NNr2CVzALS8g2CNI8JSvMjmoh234MlGJ8FgDIroun4MOgTCvQbDYN1Ww2dRV
cN3vHF1tfbQMJuFZMqBca1N5GLiAbvw=
This is my code:
import java.math.BigInteger;
import java.security.Key;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class OracleLikePasswordHasher {
public static void main(String[] args) {
System.out.println(new OracleLikePasswordHasher().generateHash("user", "password"));
}
public String generateHash(String user, String password) {
// instructions see http://www.sans.org/reading_room/special/?id=oracle_pass&ref=911
// 1. Concatenate the username and the password to produce a plaintext string;
// 2. Convert the plaintext string to uppercase characters;
// 3. Convert the plaintext string to multi-byte storage format; ASCII characters have the high byte set to 0x00;
// can I ignore 3. , cause Java uses Unicode?
String input = (user + password).toUpperCase();
for (int i = input.length(); i < 80; i++) {
input += "0";
}
try {
//4.Encrypt the plaintext string (padded with 0s if necessary to the next even block length)
//using the DES algorithm in cipher block chaining (CBC) mode with a fixed key value of
//0x0123456789ABCDEF;
byte[] keyBytes = new BigInteger("0123456789ABCDEF", 16).toByteArray();
Cipher cipherA = Cipher.getInstance("DES/CBC/NoPadding");
SecretKey key = new SecretKeySpec(keyBytes, "DES");
IvParameterSpec ips = new IvParameterSpec(new byte[] {0,0,0,0,0,0,0,0});
cipherA.init(Cipher.ENCRYPT_MODE, key, ips);
byte[] encryptedBytes = cipherA.doFinal(input.getBytes());
//5.Encrypt the plaintext string again with DES-CBC, but using the last block of the output
//of the previous step (ignoring parity bits) as the encryption key.
encryptedBytes = addParity(encryptedBytes);
Key encryptedKey = new SecretKeySpec(encryptedBytes, "DES");
Cipher cipherB = Cipher.getInstance("DES/CBC/NoPadding");
cipherB.init(Cipher.ENCRYPT_MODE, encryptedKey, ips);
byte[] encryptedPw = cipherB.doFinal(input.getBytes());
//The last block of the output is converted into a printable string to produce the password hash value.
return new sun.misc.BASE64Encoder().encode(encryptedPw);
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
// copied from http://www.exampledepot.com/egs/javax.crypto/MakeDes.html
// Takes a 7-byte quantity and returns a valid 8-byte DES key.
// The input and output bytes are big-endian, where the most significant
// byte is in element 0.
private byte[] addParity(byte[] in) {
byte[] result = new byte[8];
// Keeps track of the bit position in the result
int resultIx = 1;
// Used to keep track of the number of 1 bits in each 7-bit chunk
int bitCount = 0;
// Process each of the 56 bits
for (int i=0; i<56; i++) {
// Get the bit at bit position i
boolean bit = (in[6-i/8]&(1<<(i%8))) > 0;
// If set, set the corresponding bit in the result
if (bit) {
result[7-resultIx/8] |= (1<<(resultIx%8))&0xFF;
bitCount++;
}
// Set the parity bit after every 7 bits
if ((i+1) % 7 == 0) {
if (bitCount % 2 == 0) {
// Set low-order bit (parity bit) if bit count is even
result[7-resultIx/8] |= 1;
}
resultIx++;
bitCount = 0;
}
resultIx++;
}
return result;
}
}