为什么我的 Python 和 Objective-C 代码得到了不同的 HMAC-SHA1 结果?

3

我正在编写一个需要签名的客户端/服务器项目。我使用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=

结论:

  1. 要签名的消息应该使用双方都能识别的utf-8字符串进行编码。
  2. (感谢DJV)在Python 3中,字符串都是Unicode,因此应该使用“b”或bytearray(感谢Burhan Khalid),或编码为utf-8字符串。

根据此网站的结果,你的朋友是正确的。 - dmg
嗯,那很奇怪,你的代码在Python 2上也会产生7FH0NG0Ou4nb5luKUyjfrdWunos =。你是在使用Python 3吗? - dmg
2
好的,最后一次跟进。如果您正在使用Python 3,则应该像这样调用它:get_signature(b'KEY', b'TEXT'),因为stringunicode - dmg
1个回答

3
你的朋友是完全正确的,但你也是(有点)。你的函数在Python 2和Python 3中都是完全正确的。然而,在Python 3中,你的调用有些错误。你知道,在Python 3中,字符串 是Unicode编码的,因此为了传递ASCII字符串(像你的Objective C朋友一样,并且在Python 2中你可能会这样做),你需要使用以下方式调用你的函数:
get_signature(b'KEY', b'TEXT')

为了指定这些字符串是 字节,也就是 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())

2
这个答案是正确的,但是要在你的方法中应用它,你应该使用 bytearray(key)bytearray(msg) - Burhan Khalid

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