1

Following the guideline given in SMTP with CRAM-MD5 in Java I wrote a small program in Python to calculate the response when given the nonce as input:

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])                

However, the SMTP server rejects the response I give generated by this program. Can some one please point out what I am doing wrong?

4

3 回答 3

2

CRAM-MD5 与 HMAC 的示例实现。用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()
于 2014-05-05T22:05:55.913 回答
1

您可以使用hmac 模块来计算它,或者至少仔细检查您的输出。

您使用的是 Python2.x 还是 3.x?您也可能有一些字节/字符串问题。

具体来说,在字节处理之后 pwd.decode() 可能会给您带来垃圾,因为它试图理解不再是字符数据的东西。

您似乎还缺少将关键块扩展到散列函数输入块大小的倍数的步骤。

HMAC的维基百科文章包含一个可能有用的 Python 小示例。

于 2012-07-23T21:15:26.817 回答
0

我分析了您的代码并发现了错误:

  1. 您不仅需要对密码进行异或运算,还需要对所有 64 字节进行异或运算
  2. 由于 md5.update() 适用于二进制数据,因此不应解码密钥
  3. 出于同样的原因,您需要调用 m1.digest() 而不是 m1.hexdigest()

您的代码与我的修复和 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)

于 2014-05-05T22:44:28.270 回答