XMPP SASL SCRAM-SHA1身份验证

Wat*_*rds 12 authentication xmpp sasl sasl-scram

最近,根据以下两个网站上的说明,我能够在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.任何帮助,将不胜感激.

xny*_*hps 58

SCRAM-SHA-1

该机制如何工作的基本概述是:

  • 客户端将要验证的用户名发送为.
  • 服务器发回该用户的salt和迭代次数(通过生成它们或在其数据库中查找给定用户名).
  • 对于给定的迭代次数,客户端使用给定的盐哈希密码.
  • 客户端将结果发回.
  • 服务器执行散列的变体并将结果发送回客户端,因此客户端还可以验证服务器是否具有密码/密码的散列.

您需要的加密算法是SHA-1,HMAC与SHA-1和PBKDF2与SHA-1.您应该查看如何在您的语言/框架中使用它们,因为我不建议从头开始实现它们.

详细地

  1. 首先规范化密码(使用SASLprep),这将是normalizedPassword.这是为了确保UTF8编码不能包含相同密码的变体.
  2. 选择一个随机字符串(例如32个十六进制编码的字节).这将是clientNonce.
  3. initialMessageIS "n=" .. username .. ",r=" .. clientNonce(我使用..字符串连接).
  4. 客户端将GS2 header("n,,")添加到initialMessage并对结果进行base64编码.它将此作为第一条消息发送:

    <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="SCRAM-SHA-1">
        biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl
    </auth>
    
    Run Code Online (Sandbox Code Playgroud)
  5. 服务器以挑战回应.挑战的数据是base64编码的:

    <challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
        cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY=
    </challenge>
    
    Run Code Online (Sandbox Code Playgroud)
  6. 客户端base64解码它:

    r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
    
    Run Code Online (Sandbox Code Playgroud)
  7. 客户端解析这个:

    • r=这是serverNonce.客户端务必确保它clientNonce以其初始消息中发送的内容开头.
    • s=这是saltbase64编码的(是的,这是base64编码的两次!)
    • i=这是迭代次数,i.
  8. 客户计算:

    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)
    
    Run Code Online (Sandbox Code Playgroud)
  9. 客户端base64对其进行编码clientFinalMessage并将其作为响应发送:

    <response xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
        Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0=
    </response>
    
    Run Code Online (Sandbox Code Playgroud)
  10. 如果一切顺利,您将从<success>服务器获得响应:

     <success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>
         dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289
     </success>
    
    Run Code Online (Sandbox Code Playgroud)
  11. Base64解码包含:

     v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
    
    Run Code Online (Sandbox Code Playgroud)
  12. 客户端必须确保值v的base64编码serverSignature.

附加功能

这是算法的基本版本.您可以将其扩展为:

  • 频道绑定.这将来自TLS连接的一些信息混合到程序中以防止MitM攻击.
  • 散列存储.如果服务器始终发送相同的值salti值,则客户端只能存储saltedPassword而不是用户的密码.这更安全(因为客户端不需要存储密码,只需要很难反转盐水哈希)并且速度更快,因为客户端不需要每次都进行所有的密钥扩展.

    该服务器还可以使用哈希存储:服务器只能存储salt,i,storedKeyserverKey.关于这里的更多信息.

  • 可能还要添加SCRAM-SHA-256(尽管服务器支持似乎不存在).

陷阱

一些常见的陷阱:

  • 不要假设任何关于随机数的长度或salt(虽然如果你生成它们,确保它们足够长并且加密随机).
  • salt是base64编码,并且可以包含任何数据(嵌入NUL或多个).
  • 不使用SASLprep可能对使用ASCII密码的人很好,但它可能会完全破坏使用其他脚本的人的登录.
  • initialMessage部分authMessage不包括GS2标头(在大多数情况下,这是"n,,").

测试向量

如果您想测试您的实现,以下是RFC中示例的所有中间结果:

  • 用户名: user

  • 密码: pencil

  • 客户端生成随机现时 fyko+d2lbbFgONRv9qkxdawL

  • 初步讯息: n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL

  • 服务器生成随机nonce 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=

  • 服务器的服务器签名(十六进制): ae617da6a57c4bbb2e0286568dae1d251905b0a4

  • 你介意我给你一个拥抱/亲吻/拳头爆发贴出所有中间事物的价值吗?你是最好的人! (4认同)
  • 这太棒了! (2认同)