按照SMTP with CRAM-MD5 in Java中给出的指导原则,我在Python中编写了一个小程序来计算响应时将nonce作为输入:
import hashlib
from base64 import b64encode, b64decode
import sys
from decimal import *
#MD5(('secret' XOR opad), MD5(('secret' XOR ipad), challenge))
#opad - 0x5C, ipad - 0x36.
def main(nonce):
pwd = bytearray("password")
for i in range(len(pwd)):
pwd[i] = pwd[i] ^ 0x36
m1 = hashlib.md5()
m1.update(pwd.decode())
m1.update(b64decode(nonce))
m2 = hashlib.md5()
pwd = bytearray("password")
for i in range(len(pwd)):
pwd[i] = pwd[i] ^ 0x5C
m2.update(pwd.decode())
m2.update(m1.hexdigest())
print b64encode("username " + m2.hexdigest())
if __name__ == "__main__":
if (len(sys.argv) != 2):
print("ERROR usage: smtp-cram-md5 <nonce>")
else:
main(sys.argv[1])
但是,SMTP服务器拒绝此程序生成的响应。有人可以指出我做错了吗?
答案 0 :(得分:2)
使用HMAC的CRAM-MD5的示例实现。 用python2.7和python3.4测试。 在Python 3上,可以通过将hashlib.md5替换为'md5'来避免hashlib导入。
"""
doc-testing with example values from RFC 2195
>>> challenge = 'PDE4OTYuNjk3MTcwOTUyQHBvc3RvZmZpY2UucmVzdG9uLm1jaS5uZXQ+'
>>> user = 'tim'
>>> password = 'tanstaaftanstaaf'
>>> target_response = 'dGltIGI5MTNhNjAyYzdlZGE3YTQ5NWI0ZTZlNzMzNGQzODkw'
>>> actual_response = cram_md5(user, password, challenge)
>>> target_response == actual_response
True
"""
import base64
import hashlib
import hmac
def cram_md5(user, password, challenge):
password = password.encode('utf-8')
challenge = base64.b64decode(challenge)
digest = hmac.HMAC(password, challenge, hashlib.md5).hexdigest()
response = '{} {}'.format(user, digest).encode()
return base64.b64encode(response).decode()
if __name__ == "__main__":
import doctest
doctest.testmod()
答案 1 :(得分:1)
您可以使用hmac module来计算,或至少仔细检查输出。
您使用的是Python2.x还是3.x?您也可能遇到一些字节/字符串问题。
具体来说,在字节变换后,pwd.decode()可能会给你带来垃圾,因为它试图理解那些不再是字符数据的东西。
您似乎也缺少将密钥块扩展到散列函数的输入块大小的倍数的步骤。
HMAC的wikipedia文章在Python中包含了一个可能有用的小例子。
答案 2 :(得分:0)
我分析了你的代码,发现了错误:
您的代码包含我的修复程序和py3k兼容性:
import hashlib
from base64 import b64encode, b64decode
import sys
def main(nonce):
pwd = bytearray('password'.encode('utf-8'))
key = bytearray(64*b'\x36')
for i in range(len(pwd)):
key[i] ^= pwd[i]
m1 = hashlib.md5()
m1.update(key)
m1.update(b64decode(nonce))
m2 = hashlib.md5()
key = bytearray(64*b'\x5c')
for i in range(len(pwd)):
key[i] ^= pwd[i]
m2.update(key)
m2.update(m1.digest())
response = "username " + m2.hexdigest()
print(b64encode(response.encode('utf-8')).decode('ascii'))
if __name__ == "__main__":
if (len(sys.argv) != 2):
print("ERROR usage: smtp-cram-md5 <nonce>")
else:
main(sys.argv[1])
声明: 此代码仅对最长64字节的密码有效! (参见RFC 2195)