最近,根据以下两个网站上的说明,我能够在Swift IOS中为XMPP流提供MD5身份验证(我使用Apple的CommonCrypto C库的CC-MD5功能进行实际散列):
http://wiki.xmpp.org/web/SASLandDIGEST-MD5
http://www.deusty.com/2007/09/example-please.html
我正在寻找关于如何使其他散列SASL身份验证方案正常工作的类似解释,尤其是SCRAM-SHA1。我找到了正式的RFC5802文档,但我很难理解它(它也不是特定于XMPP)。我将非常感谢一个简单的解释或一些特定于XMPP身份验证的简单可读代码(C,PHP,C ++,Javascript,Java),它们不会将库用于实际散列以外的任何其他内容。
我对理解该过程很感兴趣,并且我不打算使用ios XMPP-Framework。任何帮助将不胜感激。
答案 0 :(得分:51)
这种机制如何运作的基本概述是:
您需要的加密算法是SHA-1,HMAC与SHA-1和PBKDF2与SHA-1。您应该查看如何在您的语言/框架中使用它们,因为我不建议从头开始实现它们。
normalizedPassword
。这是为了确保UTF8编码不能包含相同密码的变体。clientNonce
。initialMessage
是"n=" .. username .. ",r=" .. clientNonce
(我正在使用..
进行字符串连接。)客户端将GS2标头("n,,"
)添加到initialMessage,并对结果进行base64编码。它将此作为第一条消息发送:
<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="SCRAM-SHA-1">
biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl
</auth>
服务器以挑战回应。挑战的数据是base64编码:
<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY=
</challenge>
客户端base64对其进行解码:
r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
客户解析此:
r=
这是serverNonce
。客户端必须确保它以其初始消息中发送的clientNonce
开头。s=
这是salt
,base64编码(是的,这是base64编码的两次!)i=
这是迭代次数i
。客户端计算:
clientFinalMessageBare = "c=biws,r=" .. serverNonce
saltedPassword = PBKDF2-SHA-1(normalizedPassword, salt, i)
clientKey = HMAC-SHA-1(saltedPassword, "Client Key")
storedKey = SHA-1(clientKey)
authMessage = initialMessage .. "," .. serverFirstMessage .. "," .. clientFinalMessageBare
clientSignature = HMAC-SHA-1(storedKey, authMessage)
clientProof = clientKey XOR clientSignature
serverKey = HMAC-SHA-1(saltedPassword, "Server Key")
serverSignature = HMAC-SHA-1(serverKey, authMessage)
clientFinalMessage = clientFinalMessageBare .. ",p=" .. base64(clientProof)
客户端base64对clientFinalMessage
进行编码并将其作为响应发送:
<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0=
</response>
如果一切顺利,您将从服务器收到<success>
响应:
<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289
</success>
Base64解码包含:
v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
客户端必须确保v
的值是serverSignature
的base64编码。
这是算法的基本版本。您可以将其扩展为:
哈希存储。如果服务器始终发送相同的salt
和i
值,则客户端只能存储saltedPassword
而不是用户的密码。这样更安全(因为客户端不需要存储密码,只需要很难反转盐水哈希)并且速度更快,因为客户端不需要每次都进行所有的键拉伸。
服务器还可以使用散列存储:服务器只能存储salt
,i
,storedKey
和serverKey
。关于here的更多信息。
一些常见的陷阱:
salt
的长度(尽管如果你生成它们,请确保它们足够长并且加密随机)。salt
是base64编码的,可以包含任何数据(嵌入式NUL
)。initialMessage
的{{1}}部分不包含GS2标头(在大多数情况下,这是authMessage
)。如果您想测试您的实现,以下是RFC中示例的所有中间结果:
用户名:"n,,"
密码:user
客户端生成随机nonce pencil
初始讯息:fyko+d2lbbFgONRv9qkxdawL
服务器生成随机现时n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
服务器回复:3rfcNHYJY1ZVvWVs7j
盐(十六进制):r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
客户最终消息裸:4125c247e43ab1e93c6dff76
盐渍密码(十六进制):c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
客户端密钥(十六进制):1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d
存储密钥(十六进制):e234c47bf6c36696dd6d852b99aaa2ba26555728
验证消息:e9d94660c39d65c38fbad91c358f14da0eef2bd6
客户端签名(十六进制):n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
客户端证明(十六进制):5d7138c486b0bfabdf49e3e2da8bd6e5c79db613
服务器密钥(十六进制):bf45fcbf7073d93d022466c94321745fe1c8e13b
服务器签名(十六进制):0fe09258b3ac852ba502cc62ba903eaacdbf7d31
客户最终消息:ae617da6a57c4bbb2e0286568dae1d251905b0a4
服务器最终消息:c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
服务器的服务器签名(十六进制):v=rmF9pqV8S7suAoZWja4dJRkFsKQ=