Sagewire Logo

Sun's JCE 1.22 producing different MD5 MAC result to MS CryptoAPI

4 Message(s) by 2 Author(s) originally posted in java misc


From: aprz43 Date:   Tuesday, June 01, 2004
Hi Guys,

I am writing a JAVA application which needs to produce an RSA MD5 Mac
code for a message , using Sun 's JCE version 1.22.
This Mac code then gets validated bt a 3rd party application written
in C++ , which is using Microsoft 's CryptoAPI running on Windows 2000
Professional.
The problem is that the Microsoft & JAVA functions seems to produce 2
different MAC values using the same key and the same input data.

My code is as follows:

This is the JAVA Code:

public String test(String message, byte[] keyBytes) throws Exception
{
Provider sunJce = new com.sun.crypto.provider .SunJCE();
Security.insertProviderAt(sunJce,0);

//create key object
SecretKey key = new SecretKeySpec(keyBytes, "HmacMD5");

// Create a MAC object using HMAC-MD5 and initialize with key
Mac mac = Mac.getInstance("HmacMD5");
mac.init(key);

// Encode the string into bytes using utf-8 and digest it
byte[] utf8 = message.getBytes();
byte[] digest = mac.doFinal(utf8);

// If desired, convert the digest into a string
String digestB64 = new sun.misc.BASE64Encoder().encode (digest);

return digestB64;
}

*****************************************************************************

This is the C++ Code:

HRESULT CSecureDataMgr::HashData(CByteBuffer&
bufBytesData1,CByteBuffer& bufBytesData2,CByteBuffer& bufHashBuffer,
CComBSTR& bstrErrMsg)
{
HRESULT hr = S_OK;
BOOL bHashed = FALSE;

CRYPT_HASH_MESSAGE_PARA stHashParms;
BYTE * paHashBuffer = NULL;
DWORD dwHashBufferSize = DEFAULT_HASH_BUFFER_SIZE;
const BYTE* rgpbToBeHashed[3];
DWORD rgcbToBeHashed[3];

DebugTrace(_T("CSecureDataMgr::HashData() has been called. Data
buffer size is (%d) bytes. Context buffer size is (%d)
bytes."),bufBytesData1.m_dwSize,bufBytesData2.m_dwSize);

// Initialize hash ing parameters.
ZeroMemory(&stHashParms,sizeof(stHashParms));
ZeroMemory(&rgpbToBeHashed,sizeof(rgpbToBeHashed));
ZeroMemory(&rgcbToBeHashed,sizeof(rgcbToBeHashed));

// We want hash the contents of the BSTR
stHashParms.cbSize = sizeof(stHashParms);

// Set the encoding type.
stHashParms.dwMsgEncodingType = X509_ASN_ENCODING |
PKCS_7_ASN_ENCODING;

// Set the cryptographic provider name to null (this will default to
RSA)
stHashParms.hCryptProv = NULL;

// set auxillary info to null (we dont use it)
stHashParms.pvHashAuxInfo = NULL;

// Set the algorithm used to perform the hashing. Note we opt for RSA
MD5 here.
stHashParms.HashAlgorithm.pszObjId = szOID_RSA_MD5;
stHashParms.HashAlgorithm.Parameters.cbData = 0;

while (SUCCEEDED(hr) && (bufHashBuffer.m_pBytes == NULL))
{
// initialize hashing buffer structure
rgpbToBeHashed[0] = g_abCSecureDataMgrKey;
rgcbToBeHashed[0] = sizeof(g_abCSecureDataMgrKey);
rgpbToBeHashed[1] = bufBytesData1.m_pBytes;
rgcbToBeHashed[1] = bufBytesData1.m_dwSize;
rgpbToBeHashed[2] = bufBytesData2.m_pBytes;
rgcbToBeHashed[2] = bufBytesData2.m_dwSize;

// Allocate and intialize a buffer to receive the hashed value.
bufHashBuffer.Alloc(dwHashBufferSize);

// Attempt to hash the message.
bHashed = CryptHashMessage(&stHashParms,
TRUE,
3,
rgpbToBeHashed,
rgcbToBeHashed,
NULL,
NULL,
bufHashBuffer.m_pBytes,
&dwHashBufferSize);

******************************************************************************

After I convert the C++ result to a string I get a completely
different MAC value to the JAVA value.

What am I doing wrong? Are the Sun and Microsoft algorithms the same?

Thanks,

Adrian


From: Murray Date:   Tuesday, June 01, 2004
// Encode the string into bytes using utf-8 and digest it
byte[] utf8 = message.getBytes();



String.getBytes() uses the platform-default charset. Maybe consider using
String.getBytes("UTF-8 "); and ensure you do the equivalent in the C++ code


From: aprz43 Date:   Tuesday, June 01, 2004
wrote in message
> // Encode the string into bytes using utf-8 and digest it
> byte[] utf8 = message.getBytes();
String.getBytes() uses the platform-default charset. Maybe consider using
String.getBytes("UTF-8"); and ensure you do the equivalent in the C++ codeThanks Murray, but I tried that and the result was the same.





From: aprz43 Date:   Tuesday, June 15, 2004
wrote in message
wrote in message
> > // Encode the string into bytes using utf-8 and digest it
> > byte[] utf8 = message.getBytes();
>
> String.getBytes() uses the platform-default charset. Maybe consider using
> String.getBytes("UTF-8"); and ensure you do the equivalent in the C++ code
Thanks Murray, but I tried that and the result was the same.



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 Microsoft Crypto API (application programming interface)
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");

Byte ArrayOutputStream 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



Next Message: Begginner to JAVA



Programming | Sports | Autos

copyright 2006
Valid XHTML 1.0 Transitional