HMAC-Objective-CでのPHPアルゴリズムの実装
-
06-07-2019 - |
質問
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つの実装は同じ結果を返すため、ここでは1つだけが有用です。
解決
前回の回答では、PHPコードはHMACアルゴリズムの非標準のバリアントを実装しています。このObjective Cコードはそれを模倣する必要があります。 Mac OS X 10.4.11でPHPコードに対して2つの組み合わせでテストしました。
"短いキー"
" some text"
"これは非常に長いキーです。重要な部分である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;
}
他のヒント
まず、&#8220; ObjC2&#8221; MD5ではなくSHA1を使用しています(これは、SHA1が20バイト、MD5が16バイトであるため、おそらくバッファオーバーランが発生していることを意味します)。
第二に、PHP HMAC実装には非標準のバリエーションがあると思います。最後のmd5()の呼び出し以外はすべてpack(&quot; H *&quot;、&#8230;)でラップされていることに注意してください。つまり、Base64エンコードの直前の最後の1つを除きます。これは、PHPコードが、「raw」値(16バイト)ではなく、データの「印刷可能な16進」表現(32バイト、ASCIIの16進数字)をエンコードするBase64であることを意味すると思います。
PHP実装を変更することはできないため、Objective Cでこれと同じ非標準のバリエーションを持つHMAC実装を作成する必要があります。