CRAM-MD5身份验证期间的不同哈希值

时间:2017-06-27 16:14:23

标签: go hash

作为一项练习,我试图在Go中实现带有CRAM-MD5身份验证的模拟SMTP服务器(不遵循RFC 2195,因为它看起来对客户端来说并不重要我们还假设只有一个用户" bob"密码"传递")。但我似乎无法做到正确,因为响应中的哈希总是与我在服务器上的哈希不同。我使用Go发送电子邮件(将其作为单独的包运行):

{...}
smtp.SendMail("localhost:25", smtp.CRAMMD5Auth("bob", "pass"),
   "bob@localhost", []string{"alice@localhost"}, []byte("Hey Alice!\n"))
{...}

这是我从客户端收到身份验证确认后的操作:

{...}
case strings.Contains(ms, "AUTH CRAM-MD5"):
    rndbts = make([]byte, 16) // Declared at package level
    b64b := make([]byte, base64.StdEncoding.EncodedLen(16))
    rand.Read(rndbts)
    base64.StdEncoding.Encode(b64b, rndbts)
    _, err = conn.Write([]byte(fmt.Sprintf("334 %x\n", b64b)))
{...}

这就是我对客户的回应所做的事情:

{...}
 {
    ms = strings.TrimRight(ms, "\r\n") // The response to the challenge
    ds, _ := base64.StdEncoding.DecodeString(ms)
    s := strings.Split(string(ds), " ")
    login := s[0] // I can get the login from the response.
    h := hmac.New(md5.New, []byte("pass"))
    h.Write(rndbts)
    c := make([]byte, 0, ourHash.Size()) // From smtp/auth.go, not sure why we need this.
    validPass := hmac.Equal(h.Sum(c), []byte(s[1]))
    {...}   
}
{...}

validPass永远不会true。为简洁起见,我省略了摘录中的错误处理,但它们在实际代码中存在(尽管它们总是nil)。为什么哈希不同?我看过net / smtp的源代码,在我看来,我正朝着正确的方向前进,但并不完全。

1 个答案:

答案 0 :(得分:1)

我希望这有帮助! Runnable版本位于https://play.golang.org/p/-8shx_IcLV。另请注意,您需要修复%x(应为%s),以便向主机发送正确的质询。现在我想你正在尝试对你的base64字符串进行十六进制编码。

一旦解决了这个问题,我相信这段代码可以帮助您在服务器上构建正确的响应字符串,并将其与客户端发送的字符串进行比较。

// Example values taken from http://susam.in/blog/auth-cram-md5/

challenge := []byte("<17893.1320679123@tesseract.susam.in>")
username := []byte("alice")
password := []byte("wonderland")
clientResponse := []byte("YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=")

// hash the challenge with the user's password
h := hmac.New(md5.New, password)
h.Write(challenge)
hash := h.Sum(nil)

// encode the result in lowercase hexadecimal
hexEncoded := hex.EncodeToString(hash)

// prepend the username and a space
toEncode := []byte(string(username) + " " + hexEncoded)

// base64-encode the whole thing
b64Result := make([]byte, base64.StdEncoding.EncodedLen(len(toEncode)))
base64.StdEncoding.Encode(b64Result, toEncode)

// check that this is equal to what the client sent
if hmac.Equal(b64Result, clientResponse) {
    fmt.Println("Matches!")
}