Question

I have to implement the HMAC MD5 in my iPhone app. The PHP version of the algorithm (implemented server side for verification) is here and I can't modify it (it's an API)

function hmac($key, $data) {
    $b = 64; // byte length for md5
    if (strlen($key) > $b) {
        $key = pack("H*",md5($key));            
    }
    $key = str_pad($key, $b, chr(0x00));
    $ipad = str_pad('', $b, chr(0x36));
    $opad = str_pad('', $b, chr(0x5c));
    $k_ipad = $key ^ $ipad ;
    $k_opad = $key ^ $opad;
    $message = $k_opad . pack("H*",md5($k_ipad . $data));
    return base64_encode(md5($message));
}

I found a couple of objective-C implementation :

- (NSString *)HMACMD5WithKey:(NSString *)key andData:(NSString *)data
{

    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];   
    const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
    unsigned char cHMAC[CC_MD5_DIGEST_LENGTH];
    CCHmac(kCCHmacAlgMD5, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
    NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
    NSString *hash = [Base64 encode:HMAC];
    return hash;
}

Didn't return the same results (PHP != ObjC).

I played with ObjC implementations changing the digest length to 32 (result then has the same length than PHP implementation), the key length to 64 (corresponding to the first str_pad) but the results are always differents.

Can someone tell me how to have the same result in Objective-C ??

Edit: since the 2 implementations in ObjC return the same result only one is useful here..

Was it helpful?

Solution

As per my previous answer, the PHP code implements a non-standard variant of the HMAC algorithm. This Objective C code should imitate it. I tested it on Mac OS X 10.4.11 against the PHP code for two combinations:

"a short key"
"some text"

"This is a very long key. It is longer than 64 bytes, which is the important part."
"This is some very long text. It is longer than 64 bytes, which is the important part."

- (NSString *)HMACMD5WithKey:(NSString *)key andData:(NSString *)data {
    const char *cKey  = [key cStringUsingEncoding:NSASCIIStringEncoding];
    const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
    const unsigned int blockSize = 64;
    char ipad[blockSize], opad[blockSize], keypad[blockSize];
    unsigned int keyLen = strlen(cKey);
    CC_MD5_CTX ctxt;
    if(keyLen > blockSize) {
        //CC_MD5(cKey, keyLen, keypad);
        CC_MD5_Init(&ctxt);
        CC_MD5_Update(&ctxt, cKey, keyLen);
        CC_MD5_Final((unsigned char *)keypad, &ctxt);
        keyLen = CC_MD5_DIGEST_LENGTH;
    } else {
        memcpy(keypad, cKey, keyLen);
    }
    memset(ipad, 0x36, blockSize);
    memset(opad, 0x5c, blockSize);

    int i;
    for(i = 0; i < keyLen; i++) {
      ipad[i] ^= keypad[i];
      opad[i] ^= keypad[i];
    }

    CC_MD5_Init(&ctxt);
    CC_MD5_Update(&ctxt, ipad, blockSize);
    CC_MD5_Update(&ctxt, cData, strlen(cData));
    unsigned char md5[CC_MD5_DIGEST_LENGTH];
    CC_MD5_Final(md5, &ctxt);

    CC_MD5_Init(&ctxt);
    CC_MD5_Update(&ctxt, opad, blockSize);
    CC_MD5_Update(&ctxt, md5, CC_MD5_DIGEST_LENGTH);
    CC_MD5_Final(md5, &ctxt);

    const unsigned int hex_len = CC_MD5_DIGEST_LENGTH*2+2;
    char hex[hex_len];
    for(i = 0; i < CC_MD5_DIGEST_LENGTH; i++) {
        snprintf(&hex[i*2], hex_len-i*2, "%02x", md5[i]);
    }

    NSData *HMAC = [[NSData alloc] initWithBytes:hex length:strlen(hex)];
    NSString *hash = [Base64 encode:HMAC];
    [HMAC release];
    return hash;
}

OTHER TIPS

First, your “ObjC2” is using SHA1, not MD5 (this means you are probably getting a buffer overrun since SHA1 is 20 bytes, while MD5 is 16 bytes).

Second, I think there is a non-standard variation in your PHP HMAC implementation. Notice how all but the last invocations of md5() are wrapped with a pack("H*",…)? All but the last one right before Base64 encoding, that is. I think this means that the PHP code is Base64 encoding the 'printable hex' representation of the data (32 bytes, each a hex digit in ASCII) not the 'raw' value (16 bytes).

Since you cannot change the PHP implementation, you will have to write an HMAC implementation in Objective C that has this same non-standard variation.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top