CRAM-MD5认证期间出现不同的哈希值。

huangapple go评论67阅读模式
英文:

Different hashes during CRAM-MD5 authentication

问题

作为练习,我正在尝试使用Go实现一个带有CRAM-MD5身份验证的模拟SMTP服务器(不遵循RFC 2195,因为客户端似乎不关心预先哈希的挑战的格式;我还假设只有一个用户名为"bob",密码为"pass"的用户)。但是,我似乎无法正确处理,因为响应中的哈希值总是与服务器上的不同。我使用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) // 在包级别声明
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") // 挑战的响应
ds, _ := base64.StdEncoding.DecodeString(ms)
s := strings.Split(string(ds), " ")
login := s[0] // 我可以从响应中获取登录名。
h := hmac.New(md5.New, []byte("pass"))
h.Write(rndbts)
c := make([]byte, 0, ourHash.Size()) // 来自smtp/auth.go,不确定为什么需要这个。
validPass := hmac.Equal(h.Sum(c), []byte(s[1]))
{...}
}
{...}

validPass永远不为true。出于简洁起见,我在摘录中省略了错误处理,但实际代码中是有的(尽管它们始终为nil)。为什么哈希值不同?我查看了net/smtp的源代码,我觉得我走在正确的方向上,但还不够准确。

英文:

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!\n"))
{...}

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))
	rand.Read(rndbts)
	base64.StdEncoding.Encode(b64b, rndbts)
	_, err = conn.Write([]byte(fmt.Sprintf("334 %x\n", b64b)))
{...}

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

{...}
 {
	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]))
	{...}	
}
{...}

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

得分: 1

希望这能帮到你!可运行版本在https://play.golang.org/p/-8shx_IcLV。还要注意,你需要修复%x(应该是%s),这样你才能将正确的挑战发送给客户端。目前我认为你正在尝试对你的base64字符串进行十六进制编码。

一旦你修复了这个问题,我相信这段代码应该能帮助你在服务器上构建正确的响应字符串,并将其与客户端发送的内容进行比较。

// 示例值取自http://susam.in/blog/auth-cram-md5/

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

// 使用用户的密码对挑战进行哈希处理
h := hmac.New(md5.New, password)
h.Write(challenge)
hash := h.Sum(nil)

// 将结果转换为小写十六进制
hexEncoded := hex.EncodeToString(hash)

// 在用户名和一个空格之前添加
toEncode := []byte(string(username) + " " + hexEncoded)

// 对整个内容进行base64编码
b64Result := make([]byte, base64.StdEncoding.EncodedLen(len(toEncode)))
base64.StdEncoding.Encode(b64Result, toEncode)

// 检查这是否与客户端发送的内容相等
if hmac.Equal(b64Result, clientResponse) {
fmt.Println("匹配!")
}

英文:

I hope this helps! Runnable version is at https://play.golang.org/p/-8shx_IcLV. 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 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!")
}

huangapple
  • 本文由 发表于 2017年6月28日 00:14:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/44785181.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定