为什么我的 Python 和 Objective-C 代码生成的 hmac-sha1 结果不同?
我正在写一个客户端/服务器项目,需要生成一个签名。我使用 base64(hmac-sha1(key, data))
来生成这个签名。但是我发现我在 Python 代码和 Objective-C 代码中生成的签名是不同的:
get_signature('KEY', 'TEXT') //python get 'dAOnR2oXWP9xa4vUBdDvVXTpzQo='
[self hmacsha1:@"KEY" @"TEXT"] //obj-c get '7FH0NG0Ou4nb5luKUyjfrdWunos='
不仅 base64 的值不同,hmac-sha1 的摘要值也不同。我和我的朋友已经研究了几个小时,还是没搞明白问题出在哪里。
我的 Python 代码:
import hmac
import hashlib
import base64
def get_signature(key, msg):
return base64.b64encode(hmac.new(key, msg, hashlib.sha1).digest())
我朋友的 Objective-C 代码(复制自 HMAC-SHA1 的 Objective-C 示例代码):
(NSString *)hmac_sha1:(NSString *)key text:(NSString *)text{
const char *cKey = [key cStringUsingEncoding:NSASCIIStringEncoding];
const char *cData = [text 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 *hash = [GTMBase64 stringByEncodingData:HMAC];
return hash;
}
问题解决了:感谢大家的帮助。但我得告诉你们,真正的原因是我在我的 Python IDE 中输入了 "TE S T",而在这篇帖子中输入的是 "TE X T" :P
为了不浪费你们的时间,我做了一些测试,得到了一个更好的解决方案,基于你们的回答:
print get_signature('KEY', 'TEXT')
# 7FH0NG0Ou4nb5luKUyjfrdWunos=
print get_signature(bytearray('KEY'), bytearray('TEXT'))
# 7FH0NG0Ou4nb5luKUyjfrdWunos=
print get_signature('KEY', u'你好'.encode('utf-8')) # best solution, i think!
# PxEm7Oibj7ijZ55ko7V3isSkD1Q=
print get_signature('KEY', bytearray(u'你好'))
# TypeError: unicode argument without an encoding
print get_signature('KEY', u'你好')
# UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-1: ordinal not in range(128)
print get_signature(u'KEY', 'TEXT')
# TypeError: character mapping must return integer, None or unicode
print get_signature(b'KEY', b'TEXT')
# 7FH0NG0Ou4nb5luKUyjfrdWunos=
结论:
- 要生成签名的消息应该在两边都编码为 utf-8 字符串。
- (感谢 DJV)在 Python 3 中,字符串都是 Unicode,所以应该使用 'b',或者 bytearray(感谢 Burhan Khalid),或者编码为 utf-8 字符串。
1 个回答
3
你的朋友说得完全对,但你也没错(有点)。你的函数在Python 2和Python 3中都是正确的。不过,在Python 3中,你的调用有点问题。你看,在Python 3中,字符串是unicode格式的,所以如果你想传递一个ASCII字符串(就像你的Objective C朋友那样,或者你在Python 2中那样做),你需要这样调用你的函数:
get_signature(b'KEY', b'TEXT')
这样做是为了告诉Python这些字符串是字节,也就是ASCII字符串。
编辑:正如Burhan Khalid提到的,在Python 3中,有一种更灵活的方式来做到这一点,你可以这样调用你的函数:
get_signature(key.encode('ascii'), test.encode('ascii'))
或者你可以这样定义它:
def get_signature(key, msg):
if(isinstance(key, str)):
key = key.encode('ascii')
if(isinstance(msg, str)):
msg = msg.encode('ascii')
return base64.b64encode(hmac.new(key, msg, hashlib.sha1).digest())