Несоответствие запроса во время проверки подлинности аутентификатора на iOS

Я реализую простое приложение PoC в React Native для iOS и пытаюсь подтвердить аутентификатор с помощью библиотеки react-native-passkey, однако мне сложно проверить ответ на подтверждение, который я получаю от моего клиента на мой сервер Nest.JS, который использует зависимость @simplewebauthn/server для генерации ответы. Похоже, проблема заключается в несоответствии предоставленного поля запроса clientDataJSON и поля, сгенерированного на сервере.

Вот задача, которую я создаю:

i-1E5Fhg0so0Xd3CInFzHG7EVQfWqazTgf-m26flrsQ

А вот проблема с данными клиента, которую я извлек, сначала преобразовав строку base64 в строку utf8 и проанализировав ее с помощью JSON.parse:

{
  type: 'webauthn.create',
  challenge: 'aS0xRTVGaGcwc28wWGQzQ0luRnpIRzdFVlFmV3FhelRnZi1tMjZmbHJzUQ',
  origin: <redacted>
}

Исходник специально отредактирован. Это действительный домен, который используется моим экземпляром EC2 и имеет все конечные точки, необходимые для правильных потоков аттестации/утверждения.

Я читал в спецификации W3C , что Apple перед его подписанием встраивает в вызов какой-то хеш клиентских данных. Я не уверен, что делать с этими деталями спецификации, поскольку они слишком высокотехнологичны, чтобы мой тупица мог их понять.

Итак, мой вопрос будет заключаться в том, какие байты мне нужно объединить с моим вызовом, чтобы он выглядел так же, как тот, который возвращает мое приложение для iOS?

Я попытался получить конечную точку на своем сервере, передать эту информацию аутентификатору iOS и вернуть его ответ обратно на сервер. Я ожидал, что запрос, возвращенный в данных клиента, будет соответствовать запросу, сгенерированному сервером.


В комментариях к правильному ответу на этот конкретный вопрос я завел разговор о трудностях проверки подписи утверждения.

Вот новый вопрос по этому поводу.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

В вашем ответе у вас есть

{
  type: 'webauthn.create',
  challenge: 'aS0xRTVGaGcwc28wWGQzQ0luRnpIRzdFVlFmV3FhelRnZi1tMjZmbHJzUQ',
  origin: <redacted>
}

И здесь aS0xRTVGaGcwc28wWGQzQ0luRnpIRzdFVlFmV3FhelRnZi1tMjZmbHJzUQ будет закодирован в Base64.

Если я декодирую его, я получаю i-1E5Fhg0so0Xd3CInFzHG7EVQfWqazTgf-m26flrsQ, который совпадает с тем, что вы вставили как i-1E5Fhg0so0Xd3CInFzHG7EVQfWqazTgf-m26flrsQ, и вы говорите, что сгенерировали.

Обновление согласно комментарию

Итак, согласно комментарию, проблема также заключается в том, как проверить заявление об анонимной аттестации Apple, поэтому включите это как часть ответа.

Согласно Webauthn Apple проверяется немного иначе, чем ваша стандартная криптографическая подпись, или в соответствии со списком шагов.

  1. Убедитесь, что attStmt является допустимым CBOR, соответствующим синтаксису, определенному выше, и выполните декодирование CBOR для извлечения содержащихся полей.
  2. Объедините AuthenticatorData и clientDataHash, чтобы сформировать nonceToHash.
  3. Выполните хэш SHA-256 nonceToHash, чтобы получить одноразовый номер.
  4. Убедитесь, что nonce соответствует значению расширения с OID 1.2.840.113635.100.8.2 в credCert.
  5. Убедитесь, что открытый ключ учетных данных равен открытому ключу субъекта credCert.
  6. В случае успеха верните значения, зависящие от реализации, представляющие тип аттестации Анонимный ЦС и путь доверия аттестации x5c.

Итак, из списка мы видим, что нам нужно получить AuthenticatorData, который является частью ответа, и clientDataHash, который является частью запроса. Оба доступны через поля в Webauthn согласно вашему комментарию. Таким образом, для вас concat будет правильным, и вы затем проверите его хеш-значение по сравнению с частью credCert x5c в attStmt ответа, как описано в шагах 3 и 4.

Да, похоже, это действительно так! Однако сейчас я борюсь с другой проблемой: я не могу проверить подпись. Какой вызов мне следует использовать в качестве основы для проверки: тот, который сгенерирован на сервере или возвращен клиентом?

Constantine 30.07.2024 10:37

@Константин, подпись делается на объединении authenticatorData и clientDataHash, а не только на вызове. Это делает так, что как запрос, так и ответ обеспечивают большую безопасность. clientDataHash — это SHA-256 имеющихся у вас данных о клиенте, а authenticatorData — это часть ответа, который вы получаете от getAssertion. Я не использовал две библиотеки, которые вы используете, но предполагаю, что они обе поддерживают это «из коробки» без проверки вручную.

Asthor 30.07.2024 11:10

Похоже, что это не так, поскольку как вызов (оператор равенства в коде библиотеки), так и проверка подписи терпят неудачу. Как именно мне объединить AuthenticatorData и clientDataHash? Должен ли я просто декодировать оба из base64 в массив двоичных данных и просто добавить clientDataHash в конецuthuthentatorData? И нужно ли мне также хэшировать данные клиента с помощью SHA-256?

Constantine 30.07.2024 14:02

Это как бы превращается в другой вопрос о том, как проверить подпись с помощью ключей доступа, и я не уверен, что смогу дать полный ответ в комментариях. Я также вижу, что тип запроса — webauthn.create, а не webauthn.get, и мой комментарий был основан на get, так что это моя вина. Итак, в этом случае вы будете действовать по ссылке, указанной в вашем вопросе. Объединение authData и client будет выполняться на необработанных двоичных данных, поэтому вы хотите, чтобы данные не были в формате base64, а затем объединили два массива. Да, clientData должна быть хеширована, но это делается библиотеками перед отправкой.

Asthor 30.07.2024 14:37

Не могли бы вы помочь мне с моим кодом? Я использую встроенный криптомодуль NodeJS, и его метод проверки возвращает false. const clientDataHash = createHash('sha256').update(response.response.clientDataJSON‌​).digest(); const authenticatorData = Buffer.from(response.response.authenticatorData, 'base64'); const signature = Buffer.from(response.response.signature, 'base64'); const concat = Buffer.concat([authenticatorData, clientDataHash]); const result = verify('sha256', concat, publicKey, signature); console.info('custom verify', result);

Constantine 30.07.2024 17:41

Я добавил некоторые подробности в ответ о проверке Apple.

Asthor 31.07.2024 12:22

Что ж, вот самое интересное: оператор подтверждения, возвращаемый клиентом, имеет формат «нет», и даже несмотря на то, что оператор является действительным CBOR (я могу успешно декодировать его из CBOR), базовый объект кажется пустым. Итак, я не знаю, что делать с credCert и x5c. Может ли credCert быть просто открытым ключом, содержащимся в данных аутентификатора?

Constantine 31.07.2024 12:54

Если формат аттестации — none, то для создания нечего проверять. Вы можете контролировать это с помощью запроса, установив переменную attestation на direct в опциях вызова .create. При этом вам будет предоставлено подтверждение того, на что способен используемый вами аутентификатор. Однако не всегда вам нужно получать аттестацию или проверку при создании.

Asthor 31.07.2024 12:58

Хорошо, сейчас похоже, что задачи не меняются ни для запросов get, ни для create. Но проверка подписи по-прежнему не удалась, и поскольку формат аттестации отличается от формата Apple, я не уверен, как воссоздать исходные данные, которые использовались для подписи, и даже если бы я воссоздал данные Apple, это не удалось бы. Я застрял, и позвольте мне сказать вам, что это отстойное чувство.

Constantine 31.07.2024 17:33

Ну, на самом деле проверять нечего, чтобы проверять создание с аттестацией None. Я бы также предложил задать новый вопрос на тему получения аттестации, поскольку в комментариях довольно сложно передать всю необходимую информацию и правильно ответить.

Asthor 01.08.2024 09:15

Хорошо, я думаю, что просто отмечу ваш ответ как правильный, поскольку он ответил на мой вопрос, и добавлю ссылку на мою проблему с аттестацией в этот пост.

Constantine 01.08.2024 09:38

Я создал новый вопрос и отредактировал его со ссылкой, спасибо за ваш вклад, надеюсь увидеть вас там тоже!

Constantine 01.08.2024 10:50

Другие вопросы по теме