在iOS/Objective-c和Ruby/Rails中匹配Base64编码的HMAC-SHA1字符串

3
我知道有很多关于将NSData转换为NSString、NSData转换为Base64编码字符串、生成HMAC等的帖子,但没有一个似乎回答了如何在iOS和Rails中生成匹配的Base64编码HMAC-SHA1字符串。

使用下面的代码,签名不匹配。

iOS代码:

NSString *secret = @"xxx";
NSString *data = @"http://someurl?someparams";
const char *cKey = [secret cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *signature = [HMAC base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];

Rails 代码:
secret = "xxx";
data = "http://someurl?someparams";
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha1'), secret.encode("ASCII"), data.encode("ASCII"))
signature = Base64.encode64(hmac)
puts "HMAC #{hmac}"
puts "Signature #{signature}"

iOS的输出:

HMAC <05651433 c9a3d449 5816ded7 80bef87f dc903e4a> 
Signature BWUUM8mj1ElYFt7XgL74f9yQPko=

Rails输出:
HMAC 05651433c9a3d4495816ded780bef87fdc903e4a
Signature MDU2NTE0MzNjOWEzZDQ0OTU4MTZkZWQ3ODBiZWY4N2ZkYzkwM2U0YQ==

使用以下代码,我可以使它们匹配。但这种方法感觉很粗糙(在iOS端使用描述和字符串替换,在Rails中使用chomp):
NSString *secret = @"xxx";
NSString *data = @"http://someurl?someparams";
const char *cKey = [secret cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [data cStringUsingEncoding:NSASCIIStringEncoding];
unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];
NSString *HMACStr = [[HMAC description] stringByReplacingOccurrencesOfString:@"<" withString:@""];
HMACStr = [HMACStr stringByReplacingOccurrencesOfString:@">" withString:@""];
HMACStr = [HMACStr stringByReplacingOccurrencesOfString:@" " withString:@""];
HMAC = [HMACStr dataUsingEncoding:NSASCIIStringEncoding];
NSString *signature = [HMAC base64EncodedStringWithOptions:NSDataBase64Encoding64CharacterLineLength];
NSLog(@"HMAC %@", [HMAC description]);
NSLog(@"Signature %@", signature);

Rails代码:
secret = "xxx"
data = "http://someurl?someparams"
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha1'), secret.encode("ASCII"), data.encode("ASCII"))
signature = Base64.encode64(hmac).chomp
puts "HMAC #{hmac}"
puts "Signature #{signature}"

iOS 输出:

HMAC <30353635 31343333 63396133 64343439 35383136 64656437 38306265 66383766 64633930 33653461> 
Signature MDU2NTE0MzNjOWEzZDQ0OTU4MTZkZWQ3ODBiZWY4N2ZkYzkwM2U0YQ== 

Rails输出:

HMAC 05651433c9a3d4495816ded780bef87fdc903e4a
Signature MDU2NTE0MzNjOWEzZDQ0OTU4MTZkZWQ3ODBiZWY4N2ZkYzkwM2U0YQ==

如何在不使用hack的情况下生成匹配的签名?

1
对于任何寻找解决方案的人,我在这里发布了一个要点:https://gist.github.com/septerr/8851146 - septerr
1个回答

5

我不熟悉Rails,但是我认为

hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new('sha1'), secret.encode("ASCII"), data.encode("ASCII"))

将摘要计算为十六进制字符串,而

unsigned char cHMAC[CC_SHA1_DIGEST_LENGTH];
CCHmac(kCCHmacAlgSHA1, cKey, strlen(cKey), cData, strlen(cData), cHMAC);
NSData *HMAC = [[NSData alloc] initWithBytes:cHMAC length:sizeof(cHMAC)];

计算摘要作为NSData对象,它只是任意字节序列的包装器。

你可能应该更改Rails代码,将摘要计算为字节序列,而不是十六进制字符串(也许使用digest而不是hexdigest?)


谢谢!使用 digest 而不是 hexdigest 运作得很好。我不再需要在 iOS 端进行任何字符串替换等操作了。我仍然需要在 Rails 上去掉签名才能匹配。不确定为什么 base64 编码会在 Rails 中在末尾附加一个空格,而在 iOS 中则不会。 - septerr
似乎 OpenSSL::Digest::Digest 已被弃用:https://dev59.com/rWEi5IYBdhLWcg3whcvi - Crashalot
1
替代已弃用的OpenSSL::Digest::Digest的方法:https://dev59.com/rWEi5IYBdhLWcg3whcvi#24904850 - Crashalot

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接