英文:
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!")
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论