Java version of MS CryptDeriveKey()

I've been trying to build a Java equivalent of the MS Crypto API function 'CryptDeriveKey', and am almost, but not quite there.

My goal is to come up with a Java decryption routine that can take a string of encrypted text, combine it with a hashed keyword, and decrypt it using 3DES.

For short encrypted text strings, my routine works, but for longer ones, something goes awry.

I would welcome any ideas on how to make this work.

This matches

Value:'doggie'Encrypted: 4C62984923E42106

Value:'doggie'MS Encrypted:4C62984923E42106

This doesn't, but the first dozen or so bytes *do* match.

Value:'SomeTextToEncrypt'Encrypted: A5EF6FD341C71C8DA5E348710846491B12559380E11B077D

Value:'SomeTextToEncrypt'MS Encrypted:A5EF6FD341C71C8D67626FAC996568F84CB6EA02E8FCF9DF

The on-line documentation for CryptDeriveKey() describes the function as follows:

Let n be the required derived key length in bytes. The derived key is the first n bytes of the hash value after the hash computation has been completed by

CryptDeriveKey. If the required key length is longer than the hash value, the key is derived as follows:

1. Form a 64-byte buffer by repeating the constant 0x36 64 times. Let k be the length of the hash value that is represented by the input parameter hBaseData. Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of the buffer with the hash value that is represented by the

input parameter hBaseData.

2. Form a 64-byte buffer by repeating the constant 0x5C 64 times. Set the first k bytes of the buffer to the result of an XOR operation of the first k bytes of

the buffer with the hash value that is represented by the input parameter hBaseData.

3. Hash the result of step 1 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData parameter.

4. Hash the result of step 2 by using the same hash algorithm as that used to compute the hash value that is represented by the hBaseData parameter.

5. Concatenate the result of step 3 with the result of step 4.

6. Use the first n bytes of the result of step 5 as the derived key.

Here's my Java version of the function, based on my understanding of the above paragraphs.

publicstaticbyte[] CryptDeriveKey(byte[] hBaseData, String hashAlgorithm,int requiredLength ){

int keyLength = hBaseData.length;

byte[] derivedKey =newbyte[requiredLength];

if ( keyLength >= requiredLength ){

for(int i=0; i<requiredLength; i++){

derivedKey[i] = hBaseData[i];

}

return derivedKey;

}

byte[] buff1 =newbyte[64];

byte[] buff2 =newbyte[64];

Arrays.fill(buff1, (byte) 0x36);

Arrays.fill(buff2, (byte) 0x5C);

for (int i = 0; i >< keyLength; i++){

buff1[i] ^= hBaseData[i];

buff2[i] ^= hBaseData[i];

}

try{

MessageDigest md = MessageDigest.getInstance( hashAlgorithm );

md.reset();

// use the named algorithm to hash those buffers

byte[] result1 = md.digest(buff1);

md.reset();

byte[] result2 = md.digest(buff2);

for (int i = 0; i < requiredLength; i++){

if(i < result1.length)

derivedKey[i] = result1[i];

else

derivedKey[i] = result2[i - result1.length];

}

}catch ( NoSuchAlgorithmException nsae ){}

return derivedKey;

}

Here's a code snippet, showing the use of the CryptDeriveKey() function. (The hex() function simply returns a hex string of the byte[] array, and I'm fairly certain it's not the problem)

try{

String textToEncrypt ="SomeTextToEncrypt";

// First, use SHA1 to create a hashed byte array of some password data

String keyWord ="Quetzacoatl";

String hashAlg ="SHA1";

MessageDigest md = MessageDigest.getInstance( hashAlg );

md.reset();

md.update( keyWord.getBytes() );

byte[] hashKey = md.digest();

// param1 = source bytes

// param2 = name of source data's hash algorithm

// param3 = required length of derived key

byte[] derivedKey = CryptDeriveKey( hashKey, hashAlg, 24 );

SecretKey key =new SecretKeySpec(derivedKey,"DESede");

Cipher cipher = Cipher.getInstance("DESede");

cipher.init(Cipher.ENCRYPT_MODE, key);

byte[] result = cipher.doFinal(source);

String sEncrypted = hex( result );

System.out.println("Encrypted: " + sEncrypted);

}catch ( ... ){ ...}

Message was edited by: Mark_S

Mark_S

[6851 byte] By [Mark_Sa] at [2007-11-26 16:25:58]
# 1

Since the first block is the same for both then it looks to me like a block mode difference. Try using CBC with an IV of 8 bytes of zero.

byte[] ivBytes = new byte[8];

SecretKey key = new SecretKeySpec(derivedKey, "DESede");

IvParameterSpec iv = new IvParameterSpec(ivBytes);

Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");

cipher.init(javax.crypto.Cipher.ENCRYPT_MODE, key, iv);

sabre150a at 2007-7-8 22:50:03 > top of Java-index,Security,Cryptography...
# 2
I have goosebumpsYour suggestion worked, and I am thrilled. My next task is to fully understand *why* this worked, but I can now take this little function and run with it.Many thanks.Mark
Mark_Sa at 2007-7-8 22:50:03 > top of Java-index,Security,Cryptography...
# 3

> I have goosebumps

sabre150 does that to all of us.

sabre - I have no clue how you manage to a) answer so many questions, b) correctly, every time, c) while managing to not lose it completely the eleventy-billionth time you're telling someone to stop treating ciphertext as a string.

You have my utmost admiration, you truly do!

Grant

ggaineya at 2007-7-8 22:50:03 > top of Java-index,Security,Cryptography...
# 4
:-)
sabre150a at 2007-7-8 22:50:03 > top of Java-index,Security,Cryptography...