XMPP SASL SCRAM-SHA1认证 [英] XMPP SASL SCRAM-SHA1 Authentication
问题描述
最近,我能得到MD5认证XMPP工作流斯威夫特IOS以下以下两个网站的说明(我用苹果的CommonCrypto C库的CC-MD5函数实际哈希):
http://wiki.xmpp.org/web/SASLandDIGEST-MD5
http://www.deusty.com/2007/09/example- please.html
我在寻找如何获得工作,特别是SCRAM-SHA1散列其他SASL身份验证方案类似的解释。我已经找到了官方 RFC5802文档的,但我有很多的麻烦了解它(这是不特定于XMPP其一)。我想AP preciate一个更简单的解释或特定于不使用库比实际的散列的任何其他XMPP验证一些简单易读code(C,PHP,C ++,Java脚本,Java的)。
我有兴趣了解的过程中,我不希望使用ios的XMPP协议的框架。任何帮助将是AP preciated。
SCRAM-SHA-1
这个机制如何工作的基本概况是:
- 客户端发送它要作为验证的用户名。
- 服务器返回该用户的盐和迭代次数(通过生成它们或进行查找,然后在其数据库中为给定的用户名)。
- 客户机散列与迭代的给定数量的给定盐的密码。
- 客户端发送的结果了。
- 服务器执行散列的变化,并将其结果返回给客户端,所以客户端也可以验证服务器有口令/密码的哈希值。
您需要的加密算法SHA-1,HMAC与SHA-1和 PBKDF2 与SHA-1。你应该看看了怎么在你的语言/框架使用它们,因为我不建议从头开始实施他们。
详细
- 首先正常化密码(使用 SASL preP ),这将是
normalizedPassword
。这是为了确保UTF8编码不能包含相同的密码的变体。 - 选择一个随机字符串(例如32进制EN codeD字节)。这将是
clientNonce
。 - 的
initialMessage
是N =..用户名。,R =.. clientNonce
(我使用..
字符串连接)。 -
客户端prepends的GS2头(
N ,,
)的initialMessage和基于64位带codeS的结果。它发出这个作为它的第一个消息:<权威性的xmlns =金塔:IETF:params:一个XML:NS:XMPP协议,SASL机制=SCRAM- SHA-1>
biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl
< / AUTH> -
服务器与挑战响应。挑战的是数据连接的base64 codeD:
<挑战的xmlns =金塔:IETF:params:一个XML:NS:XMPP协议,SASL>
cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY=
< /挑战> -
客户端的base64德codeS吧:
<$p$p><$c$c>r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
-
客户端解析这样的:
-
R =
这是serverNonce
。客户端必须确保它与启动clientNonce
在其初始消息发送。 -
S =
这是盐
的base64 EN codeD(是的,这是基于64位恩codeD两次!) -
I =
这是迭代的次数,I
。
-
-
客户端计算:
clientFinalMessageBare =C = biws,R =.. serverNonce
saltedPassword = PBKDF2-SHA-1(normalizedPassword,盐,I)
clientKey = HMAC-SHA-1(saltedPassword,客户端密钥)
storedKey = SHA-1(clientKey)
authMessage = initialMessage ...,... .. serverFirstMessage,.. clientFinalMessageBare
clientSignature = HMAC-SHA-1(storedKey,authMessage)
clientProof = clientKey XOR clientSignature
serverKey = HMAC-SHA-1(saltedPassword,服务器密钥)
serverSignature = HMAC-SHA-1(serverkey,在authMessage)
clientFinalMessage = clientFinalMessageBare ..,P =...的base64(clientProof) -
客户端连接的base64 codeS的
clientFinalMessage
并把它作为响应:&LT;响应的xmlns =金塔:IETF:params:一个XML:NS:XMPP协议,SASL&GT;
Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0=
&LT; /响应&GT; -
如果一切顺利,你会得到一个
&LT;成功与GT;
来自服务器的响应:&LT;成功的xmlns =金塔:IETF:params:一个XML:NS:XMPP协议,SASL'&GT;
dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289
&LT; /成功&GT; -
Base64的德codeD包括以下内容:
V = pNNDFVEQxuXxCoSEiW8GEZ + 1RSo =
-
客户端必须确保
诉code>的值是base64编码的
serverSignature
。
其他
这是该算法的基本版本。你可以扩展它做的事:
- 频道绑定。这种混合从TLS连接步骤prevent MITM攻击的一些信息。
-
散列存储。如果服务器总是发送相同的
盐
和I
值,那么客户端只能存储saltedPassword
而不是用户的密码。这是更安全(因为客户端不需要存储密码,只是一个难以逆转的盐渍哈希)和更快,因为客户端不需要做所有的关键拉伸每一次。该服务器还可以使用哈希存储:服务器只能存储
盐
,I
,storedKey
和serverKey
。在这里的更多信息。 - 可能的话,还加入SCRAM-SHA-256(尽管服务器支持似乎不存在)。
缺陷
一些常见的陷阱:
- 请不要承担对随机数的长度或
盐
(但如果你生成它们,确保它们有足够的长度和加密随机)。 - 的
盐
都是Base64 EN codeD,并且可以包含任何数据(嵌入NUL
S)。 - 不使用SASL preP可以正常工作使用ASCII密码的人,但也可能完全打破登录使用其他脚本的人。
- 的
authMessage
不包括GS2头(在大多数情况下,这是initialMessage
部分N ,,
)。
测试向量
如果你想测试你的实现,这里都是为了从RFC的例子中间结果:
-
用户名:
用户
-
密码:
铅笔
-
客户端生成随机现时
fyko + d2lbbFgONRv9qkxdawL
-
最初的消息:
ñ,, N =用户,R = fyko + d2lbbFgONRv9qkxdawL
-
服务器生成随机现时
3rfcNHYJY1ZVvWVs7j
-
服务器回复:
R = fyko + d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,S = QSXCR + Q6sek8bf92,I = 4096
-
盐(十六进制):
4125c247e43ab1e93c6dff76
-
客户端最后的消息裸:
C = biws,R = fyko + d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
-
盐渍密码(十六进制):
1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d
-
客户端密钥(十六进制):
e234c47bf6c36696dd6d852b99aaa2ba26555728
-
存储的密钥(十六进制):
e9d94660c39d65c38fbad91c358f14da0eef2bd6
-
验证信息: <$c$c>n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j$c$c>
-
客户签名(十六进制):
5d7138c486b0bfabdf49e3e2da8bd6e5c79db613
-
客户端证明(十六进制):
bf45fcbf7073d93d022466c94321745fe1c8e13b
-
服务器密钥(十六进制):
0fe09258b3ac852ba502cc62ba903eaacdbf7d31
-
服务器签名(十六进制):
ae617da6a57c4bbb2e0286568dae1d251905b0a4
-
客户端最后的消息: <$c$c>c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=$c$c>
-
服务器最后的消息:
V = rmF9pqV8S7suAoZWja4dJRkFsKQ =
-
Server的服务器签名(十六进制):
ae617da6a57c4bbb2e0286568dae1d251905b0a4
Recently, I was able to get MD5 authentication working for XMPP streams in Swift IOS following the instructions on the following two websites (I used the CC-MD5 function of Apple's CommonCrypto C library for the actual hashing):
http://wiki.xmpp.org/web/SASLandDIGEST-MD5
http://www.deusty.com/2007/09/example-please.html
I'm searching for a similar explanation for how to get other hashed SASL authentication schemes working, especially SCRAM-SHA1. I have found the official RFC5802 document but I'm having a lot of trouble understanding it (it is not specific to XMPP either). I would appreciate a simpler explanation or some simple readable code (C, PHP, C++, Javascript, Java) specific to XMPP authentication that doesn't use libraries for anything other than the actual hashing.
I'm interested in understanding the process and am not looking to use the ios XMPP-Framework. Any help would be appreciated.
SCRAM-SHA-1
The basic overview of how this mechanism works is:
- The client sends the username it wants to authenticate as.
- The server sends back the salt for that user and the number of iterations (either by generating them or looking them up in its database for the given username).
- The client hashes the password with the given salt for the given number of iterations.
- The client sends the result back.
- The server does a variation of the hashing and sends it result back to the client, so the client can also verify that the server had the password/a hash of the password.
The cryptographic algorithms you'll need are SHA-1, HMAC with SHA-1 and PBKDF2 with SHA-1. You should look up how to use them in your language/framework, as I don't recommend implementing them from scratch.
In detail
- First normalize the password (using SASLprep), this will be
normalizedPassword
. This is to ensure the UTF8 encoding can't contain variations of the same password. - Pick a random string (for example 32 hex encoded bytes). This will be
clientNonce
. - The
initialMessage
is"n=" .. username .. ",r=" .. clientNonce
(I'm using..
for string concatenation). The client prepends the GS2 header (
"n,,"
) to the initialMessage and base64-encodes the result. It sends this as its first message:<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" mechanism="SCRAM-SHA-1"> biwsbj1yb21lbyxyPTZkNDQyYjVkOWU1MWE3NDBmMzY5ZTNkY2VjZjMxNzhl </auth>
The server responds with a challenge. The data of the challenge is base64 encoded:
<challenge xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> cj02ZDQ0MmI1ZDllNTFhNzQwZjM2OWUzZGNlY2YzMTc4ZWMxMmIzOTg1YmJkNGE4ZTZmODE0YjQyMmFiNzY2NTczLHM9UVNYQ1IrUTZzZWs4YmY5MixpPTQwOTY= </challenge>
The client base64 decodes it:
r=6d442b5d9e51a740f369e3dcecf3178ec12b3985bbd4a8e6f814b422ab766573,s=QSXCR+Q6sek8bf92,i=4096
The client parses this:
r=
This is theserverNonce
. The client MUST ensure that it starts with theclientNonce
it sent in its initial message.s=
This is thesalt
, base64 encoded (yes, this is base64-encoded twice!)i=
This is the number of iterations,i
.
The client computes:
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)
The client base64 encodes the
clientFinalMessage
and sends it as a response:<response xmlns="urn:ietf:params:xml:ns:xmpp-sasl"> Yz1iaXdzLHI9NmQ0NDJiNWQ5ZTUxYTc0MGYzNjllM2RjZWNmMzE3OGVjMTJiMzk4NWJiZDRhOGU2ZjgxNGI0MjJhYjc2NjU3MyxwPXlxbTcyWWxmc2hFTmpQUjFYeGFucG5IUVA4bz0= </response>
If everything went well, you'll get a
<success>
response from the server:<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'> dj1wTk5ERlZFUXh1WHhDb1NFaVc4R0VaKzFSU289 </success>
Base64 decoded this contains:
v=pNNDFVEQxuXxCoSEiW8GEZ+1RSo=
The client MUST make sure the value of
v
is the base64 encoding of theserverSignature
.
Extras
This is the basic version of the algorithm. You can extend it to do:
- Channel binding. This mixes in some information from the TLS connection to the procedure to prevent MitM attacks.
Hashed storage. If the server always sends the same
salt
andi
values, then the client can store onlysaltedPassword
instead of the user's password. This is more secure (as the client doesn't need to store the password, just a hard to reverse salted hash) and faster, as the client doesn't need to do all the key stretching every time.The server can also use hashed storage: the server can store only
salt
,i
,storedKey
andserverKey
. More info on that here.- Possibly, also adding SCRAM-SHA-256 (though server support seems non-existent).
Pitfalls
Some common pitfalls:
- Don't assume anything about the length of the nonces or
salt
(though if you generate them, make sure they are long enough and cryptographically random). - The
salt
is base64 encoded and can contain any data (embeddedNUL
s). - Not using SASLprep may work fine for people using ASCII passwords, but it may completely break logging in for people using other scripts.
- The
initialMessage
part of theauthMessage
does not include the GS2 header (in most situations, this is"n,,"
).
Test vectors
If you want to test your implementation, here are all the intermediate results for the example from the RFC:
Username:
user
Password:
pencil
Client generates the random nonce
fyko+d2lbbFgONRv9qkxdawL
Initial message:
n,,n=user,r=fyko+d2lbbFgONRv9qkxdawL
Server generates the random nonce
3rfcNHYJY1ZVvWVs7j
Server replies:
r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096
The salt (hex):
4125c247e43ab1e93c6dff76
Client final message bare:
c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
Salted password (hex):
1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d
Client key (hex):
e234c47bf6c36696dd6d852b99aaa2ba26555728
Stored key (hex):
e9d94660c39d65c38fbad91c358f14da0eef2bd6
Auth message:
n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
Client signature (hex):
5d7138c486b0bfabdf49e3e2da8bd6e5c79db613
Client proof (hex):
bf45fcbf7073d93d022466c94321745fe1c8e13b
Server key (hex):
0fe09258b3ac852ba502cc62ba903eaacdbf7d31
Server signature (hex):
ae617da6a57c4bbb2e0286568dae1d251905b0a4
Client final message:
c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
Server final message:
v=rmF9pqV8S7suAoZWja4dJRkFsKQ=
Server's server signature (hex):
ae617da6a57c4bbb2e0286568dae1d251905b0a4
这篇关于XMPP SASL SCRAM-SHA1认证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!