doulv1760 2017-06-27 16:14
浏览 69


As an exercise, I'm trying to implement a mock SMTP server with CRAM-MD5 authentication in Go (without following RFC 2195, since it looks like it doesn't matter to the client what format the pre-hashed challenge is in; I also assume there is only one user "bob" with password "pass"). But I can't seem to get it right as the hash in response is always different from what I have on the server. I send the email using Go as such (running it as a separate package):

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

Here's what I do when I get the authentication acknowledgement from the client:

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

And this is what I do with the client's response:

    ms = strings.TrimRight(ms, "
") // 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"))
    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]))

And the validPass is never true. I omitted error handling from the excerpts for brevity, but they're there in the actual code (though they're always nil). Why are the hashes different? I have looked at the source code for net/smtp, and it seems to me that I'm going in the right direction, but not quite.

  • 写回答

1条回答 默认 最新

  • doumi4974 2017-06-28 00:12

    I hope this helps! Runnable version is at Also note that you'll need to fix your %x (should be %s) so you're sending the right challenge down to the client. Right now I think you're trying to hex-encode your base64 string.

    Once you've fixed that, I believe this code should help you to construct the right response string on the server and compare it to what the client sent.

    // Example values taken from
    challenge := []byte("<>")
    username := []byte("alice")
    password := []byte("wonderland")
    clientResponse := []byte("YWxpY2UgNjRiMmE0M2MxZjZlZDY4MDZhOTgwOTE0ZTIzZTc1ZjA=")
    // hash the challenge with the user's password
    h := hmac.New(md5.New, password)
    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) {
    本回答被题主选为最佳回答 , 对您是否有帮助呢?



  • ¥15 双向孟德尔随机化反向是为什么显示参数长度为零
  • ¥15 用C语言完成一个复杂的游戏
  • ¥15 如何批量更改很多个文件夹里的文件名中包含文件夹名?
  • ¥50 MTK手机模拟HID鼠标出现卡顿
  • ¥20 求下下面这个数据结构代码
  • ¥20 前端 二进制文件流图片转化异常
  • ¥15 github上的这个C语言项目如何跑起来
  • ¥15 java 判断某个数 区间是否存在
  • ¥15 appium控制多个雷电模拟器问题
  • ¥15 C# iMobileDevice