Аутентификация XMPP SASL SCRAM-SHA1
недавно я смог получить аутентификацию MD5, работающую для потоков XMPP в Swift IOS, следуя инструкциям на следующих двух веб-сайтах (я использовал функцию CC-MD5 библиотеки CommonCrypto C Apple для фактического хэширования):
http://wiki.xmpp.org/web/SASLandDIGEST-MD5
http://www.deusty.com/2007/09/example-please.html
Я ищу аналогичное объяснение того, как получить другой хэшированный SASL схемы аутентификации работают, особенно SCRAM-SHA1. Я нашел чиновника RFC5802 документ, но у меня много проблем с его пониманием (это не относится и к XMPP). Я был бы признателен за более простое объяснение или простой читаемый код (C, PHP, C++, Javascript, Java), специфичный для аутентификации XMPP, который не использует библиотеки ни для чего, кроме фактического хэширования.
Я заинтересован в понимании процесса и не собираюсь использовать ios XMPP-Framework. Любая помощь будет оценена.
1 ответов
ПРОВАЛИВАЙ-ША-1
основной обзор того, как работает этот механизм:
- клиент отправляет имя пользователя, которое он хочет аутентифицировать как.
- сервер отправляет обратно соль для этого пользователя и количество итераций (либо путем их генерации, либо путем поиска их в своей базе данных для данного имени пользователя).
- клиент хэширует пароль с солью для заданного числа итераций.
- клиент отправляет результат обратно.
- сервер выполняет изменение хэширования и отправляет его результат обратно клиенту, поэтому клиент также может проверить, что у сервера был пароль/хэш пароля.
криптографические алгоритмы, которые вам понадобятся, - это SHA-1, HMAC с SHA-1 и PBKDF2 с с SHA-1. Вы должны посмотреть, как использовать их в своем языке/фреймворке, поскольку я не рекомендую реализовывать их с нуля.
In деталь
- сначала нормализуйте пароль (используя SASLprep), это будет
normalizedPassword
. Это необходимо для того, чтобы кодировка UTF8 не могла содержать варианты одного и того же пароля. - выбрать случайную строку (например, 32 байта в шестнадцатиричной кодировке). Это будет
clientNonce
. - на
initialMessage
is"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
является кодировкой base64serverSignature
.
дополнительно
это базовая версия алгоритма. Вы можете расширить его, чтобы сделать:
- привязка канала. Это смешивает некоторые сведения из подключения TLS к процедуре для предотвращения MitM атаки.
-
хэшированных хранения. Если сервер всегда отправляет одно и то же
salt
иi
значения, тогда клиент может хранить толькоsaltedPassword
вместо пароля пользователя. Это более безопасно (так как клиенту не нужно хранить пароль, просто трудно отменить соленый хэш) и быстрее, так как клиенту не нужно каждый раз выполнять растяжку ключей.сервер также может использовать хэшированное хранилище: сервер может хранить только
salt
,i
,storedKey
иserverKey
. Более подробную информацию о том, что здесь. - возможно, также добавляя SCRAM-SHA-256 (хотя поддержка сервера кажется несуществующей).
подводные камни
некоторые "подводные камни":
- не предполагайте ничего о длине nonces или
salt
(хотя если вы их генерируете, убедитесь, что они достаточно длинные и криптографически случайные). - на
salt
кодируется base64 и может содержать каких-либо данных (встроенныйNUL
s). - не использовать 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
соль (hex):
4125c247e43ab1e93c6dff76
последнее сообщение клиента голое:
c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
соленый пароль (hex):
1d96ee3a529b5a5f9e47c01f229a2cb8a6e15f7d
ключ клиента (hex):
e234c47bf6c36696dd6d852b99aaa2ba26555728
сохраненный ключ (hex):
e9d94660c39d65c38fbad91c358f14da0eef2bd6
Auth сообщение:
n=user,r=fyko+d2lbbFgONRv9qkxdawL,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,s=QSXCR+Q6sek8bf92,i=4096,c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j
подпись клиента (hex):
5d7138c486b0bfabdf49e3e2da8bd6e5c79db613
доказательство клиента (hex):
bf45fcbf7073d93d022466c94321745fe1c8e13b
ключ сервера (hex):
0fe09258b3ac852ba502cc62ba903eaacdbf7d31
подпись сервера (hex):
ae617da6a57c4bbb2e0286568dae1d251905b0a4
клиент последнее сообщение:
c=biws,r=fyko+d2lbbFgONRv9qkxdawL3rfcNHYJY1ZVvWVs7j,p=v0X8v3Bz2T0CJGbJQyF0X+HI4Ts=
сервер-последнее сообщение:
v=rmF9pqV8S7suAoZWja4dJRkFsKQ=
подпись сервера сервера (hex):
ae617da6a57c4bbb2e0286568dae1d251905b0a4