> Hi Guys,
>
> I am writing a Java application which needs to produce an RSA MD5 Mac
What is an RSA MD5 MAC? Where is it defined? The MD5 algorithm is
from RSA, but the HMAC construct is not.
> code for a message, using Sun's JCE version 1.22.
> This Mac code then gets validated bt a 3rd party application written
[quoted text clipped - 21 lines]
> // Encode the string into bytes using utf-8 and digest it
> byte[] utf8 = message.getBytes();
This getBytes is using the default encoding, not necessarily UTF-8.
> byte[] digest = mac.doFinal(utf8);
>
[quoted text clipped - 3 lines]
> return digestB64;
> }
I can't really read the C++ code, but I couldn't find the characters
"HMAC" in it. What in the code determines that it is generating an HMAC?
> After I convert the C++ result to a string I get a completely
> different MAC value to the Java value.
Dump out the keys, and the bytes of the messages, and the HMAC
outputs, in hex, in both implementations.
> What am I doing wrong? Are the Sun and Microsoft algorithms the same?
The HMAC-MD5 of the message "abc" (hex 616263), using an HMAC key of
16 bytes of binary zeros, is 0C7F795D3F611FAE11252AE8DDA603D7. Does
either of your implementations produce this?
--Mike Amling
Adrian - 15 Jun 2004 06:16 GMT
> > Hi Guys,
> >
[quoted text clipped - 55 lines]
>
> --Mike Amling
PROBLEM SOLVED!!
After 2 weeks of playing with this I managed to get the correct MD5 in
JCE hash.
These are the steps required:
1) I discovered that callintg CryptHashMessage in the MS Crypto API
with an array of strings to hash, is the same as calling the
CryptCreateHash method multiple times, OR just calling it once with a
concatenated string that contains the concatenated contents of all of
the array elements used in the CryptHashMessage call.
2) Calling CryptHashMessage using szOID_RSA_MD5 as the hash algorithm
corresponds to calling CryptCreateHash using CALG_MD5 (or 32771) as
the algorithm.
3) The difference between doing this in C++ and in Java was that I had
to put an empty byte after each byte of the Java message.
Below is my Java code which produces the same MD5 hash as the C++ code
I posted earlier:
public String getMD5Hash(String message, byte[] key) throws
IOException, NoSuchAlgorithmException
{
//set IBMJCE Provider
Provider ibmJce = new IBMJCE();
Security.insertProviderAt(ibmJce, 0);
//create MD5 digest
MessageDigest md = MessageDigest.getInstance("MD5");
ByteArrayOutputStream baos = new ByteArrayOutputStream();
java.security.DigestOutputStream dbaos = new
java.security.DigestOutputStream(baos, md);
//put null bytes after each byte in the message
//this is required to get the same result as the C++ application
byte[] nulledMessage = new byte[message.getBytes().length * 2] ;
for (int i = 0; i < message.getBytes().length; i++)
{
nulledMessage[i*2] = message.getBytes()[i];
nulledMessage[(i*2) + 1] = (byte)0;
}
//append the key to the beginning of the message
byte[] concArray = new byte[nulledMessage.length + key.length];
System.arraycopy (key, 0, concArray, 0, key.length);
System.arraycopy (nulledMessage, 0, concArray, key.length,
nulledMessage.length);
dbaos.write(concArray);
dbaos.close();
//get the MD5 digest
byte[] digest = md.digest();
//convert to hex string
return toHexString(digest);
}
I hope this helps the next person with this problem.
Happy Hashing,
Adrian
Roedy Green - 15 Jun 2004 06:48 GMT
>//put null bytes after each byte in the message
> //this is required to get the same result as the C++ application
[quoted text clipped - 4 lines]
> nulledMessage[(i*2) + 1] = (byte)0;
> }
It would seem it might be easier to tackle this from the C++ end. If
you feed it raw bytes with the same encoding, it should not be
inserting 0 bytes. Perhaps the problem is avoiding accidental 0s
which are treated a string terminators in C++.
I don't know how you discovered this way to make them generate the
same value. Did you disassemble the C++ code? The only part that was
obvious was that you wanted a plain MD5 digest, not a secret key MD5.

Signature
Canadian Mind Products, Roedy Green.
Coaching, problem solving, economical contract programming.
See http://mindprod.com/jgloss/jgloss.html for The Java Glossary.
Adrian - 16 Jun 2004 00:34 GMT
> >//put null bytes after each byte in the message
> > //this is required to get the same result as the C++ application
[quoted text clipped - 13 lines]
> same value. Did you disassemble the C++ code? The only part that was
> obvious was that you wanted a plain MD5 digest, not a secret key MD5.
I discovered this through lots and lots of trial and error.
Unfortunately I was not able to tackle this from the C++ end as I had
to conform to an existing (and untouchable) c++ application running on
a different system.
nobody - 16 Jun 2004 00:32 GMT
The reason the extra bytes are padded is that the CryptoAPI is encoding
the string using UTF-16LE (a 16-bit representation) rather than UTF-8.
So you need to do string.getBytes("UTF-16LE"). A much easier way to do
what you have below is:
public String getMD5Hash(String message, byte[] key)
throws NoSuchAlgorithmException, UnsupportedEncodingException {
MessageDigest digest = MessageDigest.getInstance("MD5");
digest.update(key);
return toHexString(digest.digest(message.getBytes("UTF-16LE")));
}
> 3) The difference between doing this in C++ and in Java was that I had
> to put an empty byte after each byte of the Java message.
[quoted text clipped - 46 lines]
>
> Adrian