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