3

我必须在我的 iPhone 应用程序中实现 HMAC MD5。算法的PHP版本(实现服务器端进行验证)在这里,我不能修改它(它是一个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));
}

我发现了几个objective-C实现:

- (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;
}

没有返回相同的结果(PHP!= ObjC)。

我使用 ObjC 实现将摘要长度更改为 32(结果与 PHP 实现的长度相同),密钥长度为 64(对应于第一个 str_pad),但结果总是不同的。

有人可以告诉我如何在 Objective-C 中获得相同的结果吗?

编辑:由于 ObjC 中的 2 个实现返回相同的结果,因此这里只有一个有用..

4

2 回答 2

4

根据我之前的回答,PHP 代码实现了 HMAC 算法的非标准变体。这个Objective C 代码应该模仿它。我在 Mac OS X 10.4.11 上针对 PHP 代码测试了两种组合:

“一个短键”
“一些文本”

“这是一个很长的密钥。它比 64 字节长,这是重要的部分。”
“这是一段很长的文本,超过 64 字节,这是很重要的部分。”

- (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;
}
于 2009-10-27T13:10:56.413 回答
2

首先,您的“ObjC2”使用的是 SHA1,而不是 MD5(这意味着您可能会遇到缓冲区溢出,因为 SHA1 是 20 字节,而 MD5 是 16 字节)。

其次,我认为您的 PHP HMAC 实现存在非标准变体。注意除了 md5() 的最后一次调用之外的所有调用是如何用 pack("H*",...) 包装的?就是在 Base64 编码之前的最后一个。我认为这意味着 PHP 代码是 Base64 编码数据的“可打印十六进制”表示(32 个字节,每个 ASCII 十六进制数字)而不是“原始”值(16 个字节)。

由于您无法更改 PHP 实现,因此您必须在具有相同非标准变体的 Objective C 中编写 HMAC 实现。

于 2009-10-27T08:26:29.250 回答