function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
kenfkenf 

Getting Java and Apex hash algorithms to match

I am consuming a java web service in Apex that needs a security hash computed using MD5 or SHA1, and the digest produced by the Crypto class is not matching the output of the Java algorithm for either method. Here is the Apex code, which I have broken up into several lines in order to print interim debug information:

 

public static String createHash(String preHashValue) {

System.debug('Prehashvalue: ' + preHashValue);

 

Blob bPrehash = Blob.valueOf(preHashValue);

System.debug('\n----Blob.valueOf(preHashValue): ' + bPrehash.toString());

 

String convertedPrehash = EncodingUtil.convertToHex(bPrehash);

System.debug('\nHex prehash value: ' + convertedPrehash);

 

Blob bHexPrehash = Blob.valueOf(convertedPrehash);

System.debug('\nBlob.valueOf(convertedPrehash): ' + bHexPrehash.toString());

 

Blob bsig = Crypto.generateDigest('SHA1', bHexPrehash);

System.debug('\n--------Blob version of digest: ' + bsig.toString());

 

String result = EncodingUtil.convertToHex(bsig);

System.debug('\n--------------Hex digest value: ' + result);

 

return result;

}

 

And the corresponding Java code:

 

private static String CreateHash(String preHashVal) {

java.security.MessageDigest algorithm;

String md5val = null;

try {

// algorithm = java.security.MessageDigest.getInstance("MD5");

algorithm = java.security.MessageDigest.getInstance("SHA1");

byte[] defaultBytes = preHashVal.getBytes();

System.out.println("Prehash value as bytes: '" + bytesToHexString(defaultBytes) + "'");

 

algorithm.reset();

algorithm.update(defaultBytes);

 

byte[] messageDigest = algorithm.digest();

System.out.println("Computed digest: '" + bytesToHexString(messageDigest) + "'");

 

StringBuffer hexString = new StringBuffer();

 

for (int i = 0; i < messageDigest.length; i++) {

String hex = Integer.toHexString(0xFF & messageDigest[i]);

if (hex.length() == 1) {

hexString.append('0');

}

hexString.append(hex);

}

md5val = hexString.toString();

} catch (Exception e) {

e.printStackTrace();

}

return md5val;

}

 

 

 

Using the same "prehashvalue", which is just a constant, a secret code and a random number, they produce the same values in the log output until Crypto.generateDigest('SHA1', bHexPrehash)  and algorithm.digest().

 

Since neither of these methods has a lot of flexibility in terms of options, I have no idea how to get the hash to work out on both sides. Also, the Java side is code I do not control. I would be truly obligated for your help if you can spot where I have made a mistake.

 

--k 

Message Edited by kenf on 11-11-2009 01:28 PM
Message Edited by kenf on 11-11-2009 01:32 PM
Best Answer chosen by Admin (Salesforce Developers) 
kenfkenf

I hope this can help others, but I found the source of my error. I was so concerned to verify every step of the process that I made my SFDC code look like the Java code, as if the hashing algorithms necessarily took the same inputs.

 

In Java, you pass a hex-encoded string to the algorithm to generate the hash. In Apex, you do not have to.

 

All the code needs to do is:

 

Blob bPrehash = Blob.valueOf(preHashValue); Blob bsig = Crypto.generateDigest('SHA1', bPrehash); return EncodingUtil.convertToHex(bsig);

 

In my initial version I had converted the prehash to a hex-encoded string prior to sending it to Crypto.generateDigest().

 

 

 

All Answers

SuperfellSuperfell
I would back up a couple of steps and start with fixed bytes in both and see if they differ then, then work backwards through the steps. One thing that jumps out is a probable mismatch in character encodings used to go from a string to bytes.
kenfkenf

Thanks for the quick response Simon. I used all the debug statements to verify that the values are precisely the same up to the point I have referenced. The hex conversion on the apex and Java side generated '53616c6573466f726365496e746567726174696f6e24616c657346307263333733333633313837' and

'53616c6573466f726365496e746567726174696f6e24616c657346307263333733333633313837', respectively (the same values)...

 

and the call to algorithm.digest() produced

'b77b926dee3017cb566c70f924a8d53ee06a6153' in Java,

 

while Crypto.generateDigest('SHA1', bHexPrehash) produced

'3a1aec91912459120c818f9d347c6a00f105a164' in Apex.

 

I also included a few of the debug statements to ensure that I was not faked out by an arbitrary toString implementation in the Blob log statements. The EncodingUtil.convertToHex appears to work the same as the algorithm I have to match on the Java side.

 

--k 

kenfkenf

I hope this can help others, but I found the source of my error. I was so concerned to verify every step of the process that I made my SFDC code look like the Java code, as if the hashing algorithms necessarily took the same inputs.

 

In Java, you pass a hex-encoded string to the algorithm to generate the hash. In Apex, you do not have to.

 

All the code needs to do is:

 

Blob bPrehash = Blob.valueOf(preHashValue); Blob bsig = Crypto.generateDigest('SHA1', bPrehash); return EncodingUtil.convertToHex(bsig);

 

In my initial version I had converted the prehash to a hex-encoded string prior to sending it to Crypto.generateDigest().

 

 

 

This was selected as the best answer