Понимание подписи RSA для JWT

я реализую систему входа в систему с помощью схемы JWT (JsonWebToken). В основном после входа пользователя / входа в систему сервер подписывает JWT и передает его клиенту.

затем клиент возвращает маркер с каждым запросом, и сервер проверяет маркер перед отправкой ответа.

Это довольно много, как вы ожидаете, но у меня возникли некоторые проблемы с логикой процесса. Из всех математических статей, которые я читал, кажется, что RSA для подписи используются асимметричные ключи. Поскольку открытый ключ, как следует из его имени, предоставляется клиенту, а закрытый ключ хранится на сервере, имеет смысл подписать JWT открытым ключом, который отправляется клиенту, и проверить его на стороне сервера с помощью закрытого ключа.

однако на каждом примере и библиотеке я вижу, что это, кажется, наоборот, любая идея о том, почему это так ? Если JWT подписан с закрытым ключом и проверен с открытым, чем смысл?

3 ответов


во-первых, извинения, этот ответ стал довольно длинным.

Если вы используете RSA для шифрования токенов, а подключающийся клиент является веб-браузером, клиент никогда не увидит ключи RSA (открытые или частные). Это связано с тем, что клиенту, по-видимому, не нужно проверять, что JWT действителен, только сервер должен это сделать. Клиент просто держится за JWT и показывает его серверу, когда его спрашивают. Затем сервер проверяет, чтобы убедиться, что он действителен, когда он видит знак.

Так зачем вам может понадобиться комбо открытого / закрытого ключа для JWT? Ну, во-первых, вам не нужно использовать алгоритм открытого / закрытого ключа.

вы можете кодировать JWT с несколькими различными алгоритмами, RSA является одним из них. Другими популярными вариантами кодирования JWT являются алгоритмы ECDSA или HMAC (стандарт JWT поддерживает другие). HMAC, в частности, не является схемой открытого / закрытого ключа. Есть только один ключ, ключ, который используется как для шифрования, так и расшифровки жетонов. Вы можете думать об этом как об использовании закрытого ключа для подписания и дешифрования JWT. Я никоим образом не эксперт в этом, но вот выводы, к которым я пришел из своих собственных исследований в последнее время:

использование HMAC приятно, потому что это самый быстрый вариант. Однако, чтобы расшифровать JWT, вам нужно дать кому-то один ключ, который делает все, совместное использование этого ключа с кем-то другим означает, что этот человек теперь также может знак жетоны и притворись, что они-это ты. Если вы создаете несколько серверных приложений, которые все должны иметь возможность проверять ваши JWT, вы, возможно, не захотите, чтобы каждое приложение имело возможность кодировать токены (разные программисты могут поддерживать разные приложения, совместное использование способности кодирования с большим количеством людей является риском для безопасности и т. д.). В этом случае лучше иметь один, строго контролируемый закрытый ключ (и одно приложение, которое делает подпись), а затем поделиться открытым ключом с другими людьми, чтобы дать им возможность проверить маркеры. Здесь закрытый ключ используется для шифрования токенов, а открытый ключ-для их расшифровки. В этом случае вы хотите выбрать RSA или ECDSA.

в качестве примера у вас может быть экосистема приложений, к которым все подключаются та же база данных. Чтобы войти в систему, каждое приложение отправляет людей в один, выделенный, "вход в систему" приложение. Это приложение имеет закрытый ключ. Другой приложения могут убедитесь, что пользователь вошел в систему с помощью открытого ключа (но они не могут регистрировать людей).

исследование, которое я сделал, указывает на то, что RSA является лучшим вариантом для большинства приложений JWT в этом scenerio. Это связано с тем, что ваше приложение будет, теоретически, часто проверять токены. RSA намного быстрее, чем ECDSA при проверке. ECDSA в первую очередь приятно, потому что ключи меньше по размеру. Это делает его лучше для сертификатов https, потому что вам нужно отправить открытый ключ в браузер клиента. Однако в JWT scenerio ключи остаются на сервере, поэтому размер хранилища n / a и скорость проверки более важны.

вывод: если вы создаете небольшое приложение без нескольких небольших приложений "микро-сервис" / вы единственный разработчик, вероятно, выберите HMAC для шифрования ключей. В противном случае, вероятно, выберите RSA. Опять же, я не эксперт, просто кто-то, кто недавно погуглил эту тему, поэтому возьмите это с солью.


ваше предложение:

имеет смысл подписать JWT открытым ключом, который отправляется в клиент и проверьте его на стороне сервера, используя закрытый ключ.

неверно. Подписание выполняется с помощью закрытого ключа сервера, шифрование выполняется с помощью открытого ключа клиента. Так работает PKI в целом.


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

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

зашифровать данные с помощью открытого ключа и расшифровки с закрытым ключом. Это звучит противоположно, но на самом деле следует той же логической концепции, что и подпись. Если вы хотите отправить данные между человеком A и человеком B, у обоих людей есть пара открытых/закрытых ключей, и они делятся своими открытыми ключами друг с другом при встрече (рукопожатие). A создает сообщение для B и шифрует его с помощью открытого ключа B и отправляет его B. теперь никто без закрытого ключа B не может расшифровать это сообщение, включая A - даже если они изначально отправили его.

с точки зрения JWT, полезная нагрузка JWT сама по себе просто кодируется Base64 JSON с некоторыми стандартизированными полями. Подпись позволяет кому-то с открытым ключом проверить, что информация не была изменена кем-то посередине. Подобно контрольной сумме, но с некоторыми дополнительными чувствами безопасности, основанными на теплых нечетких чувствах. Содержимое подписанного JWT легко видно (base64 кодирует как unicode или utf-8, а не шифрование) конечному пользователю и любому в середине, поэтому обычно не одобряется отправка конфиденциальных данных в JWT, таких как пароли или PII.

Как уже упоминалось, большинство JWTs содержат информацию, не предназначенную для клиентов, но помогающую облегчить апатридную часть услуг RESTful. Обычно JWT будет содержать accountid, userid и часто разрешения как "утверждения". Конечная точка API может проверить подпись и разумно доверять утверждениям, которые не будут изменены клиентом. Наличие клиента, отправляющего JWT для каждого запроса, сохраняет конечную точку, которая должна делать много базы данных взад и вперед, чтобы получить где они находятся, просто проверяя подпись с помощью открытого ключа.

кроме того, подписанные JWTs могут быть зашифрованы. Согласно JWE spec, полезная нагрузка шифруется после подписания, а затем расшифровывается перед проверкой. Компромисс здесь заключается в том, что все конечные точки также должны иметь закрытый ключ для расшифровки JWT, но конечные пользователи не смогут видеть содержимое JWT. Я говорю компромисс, потому что в целом закрытые ключи предназначены для обеспечения безопасности и широкого распространения закрытый ключ просто менее безопасен. Безопасность, оценка рисков и стоимость/выгода шифрования-это совсем другое животное:)