> Hi Mike,
>
[quoted text clipped - 4 lines]
> There is an existing c++ application which creates a MAC signature /
> hash for a message using the MS CryptoAPI.
"MAC", "signature" and "hash" are all different, and it does not pay
to confuse them.
A hash has no key.
A MAC has one key, which is used both to form the Message
Authentication Code of a message and to verify the MAC of that message.
A signature has two keys, a private key which is needed to form the
signature of a message and a public key, which is used to verify the
signature of that message.
> It calls the
> CryptHashMessage method on the API with a hardcoded 256 byte key and
[quoted text clipped - 66 lines]
> able to create a MAC for a message in Java, which can then be
> re-created by the C++).
Well, let me tell you, I can't tell from looking at this code if it's
finding a hash or a MAC, and if it's a MAC, what the MAC algorithm is.
If it's the HMAC-MD5 algorithm, I would expect that the characters
"hmac" would appear in the source somewhere.
So, the first step I would take is to make sure we know what
algorithm it is we want to duplicate in Java. If the algorithm is
HMAC-MD5 (which you've mentioned in previous posts) with a 128-bit key,
then the first question is whether the above code puts out hex
0C7F795D3F611FAE11252AE8DDA603D7 as the HMAC-MD5 of the 24-bit message
hex 616263 when the HMAC key is 128 zero bits. I'd follow that with a
couple more experiments to make sure we understand the endian
conventions. E.g., with the 128-bit key hex
00000000000000000000000000000001, the HMAC-MD5 of that same message hex
616263 is hex CC2380E605E833C027928AD3F57646C7.
If the C++ code puts out something else, then we have to discover
what algorithm it's using.
> I have tried using a Mac object, instantiating it with the same key
> and calling the "HmacMD5" algorithm. The hash I receive in Java is
> different to the c++ value...
What does your Java implementation say the HMAC-MD5 MAC is for
message hex 616263 with key hex 00000000000000000000000000000000?
> I tried doing some tricks like reversing
> the byte order and adding a null byte to the end of the message.
[quoted text clipped - 6 lines]
> However, all I have available to me is the hardcoded 256 byte array
> that the C++ app is using.
There are lots of ways to represent an RSA private key. Some just
have a value of d. Some include p and q. Some have the inverses of e mod
p and mod q for use with the Chinese Remainder Theorem. Some include
bits that describe which of d, p, q, e**-1 mod p, e**-1 mod q are
included. It's unlikely you'd be able to take 256 bytes that is
allegedly an RSA private key and use it successfully without knowing its
structure.
> To be honest I'm not even sure if I'm on the right track here.
> "MD5withRSA" sounds very similar to the szOID_RSA_MD5 which the C++ is
[quoted text clipped - 38 lines]
>>>
>>>Adrian

Signature
--Mike Amling
Adrian - 15 Jun 2004 06:16 GMT
> > Hi Mike,
> >
[quoted text clipped - 170 lines]
> >>>
> >>>Adrian
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