Вопрос или проблема
Я пишу приложение на C# .NET. В моем приложении данные пользователей должны быть зашифрованы в базе данных. Пользователи также должны иметь возможность делиться данными с другими пользователями. Для этой цели я хочу использовать шифрование RSA.
Моя предполагаемая реализация такова: открытый ключ пользователя хранится в базе данных. Данные, которые должны быть поделены с другим пользователем, шифруются с помощью открытого ключа этого пользователя и хранятся в базе данных. Теперь у меня есть две проблемы с этим подходом:
1.) Я хочу, чтобы пара открытого/закрытого ключа была получена из пароля пользователя, чтобы не хранить ее небезопасно на диске. Каждый раз, когда пользователь входит в приложение, закрытый ключ генерируется, и данные пользователя могут быть зашифрованы/расшифрованы. Мой вопрос: как я могу получить пару ключей RSA из пароля?
2.) Если пользователь сбросит свой пароль, зашифрованные ранее данные уже не могут быть расшифрованы, так как для этого потребуется старый пароль пользователя для получения закрытого ключа. Насколько я знаю, одно из решений этой проблемы заключается в том, чтобы шифровать/расшифровывать данные с помощью случайно сгенерированного ключа шифрования, который никогда не изменяется, а затем зашифровать этот ключ с помощью ключа на основе пароля. Но тогда как пользователь может делиться зашифрованными данными с другими пользователями? Для этого пользователю также придется поделиться своим ключом на основе пароля, чтобы предоставить доступ к общему ключу шифрования данных, что, очевидно, противоречит самой сути шифрования.
Итак, как мне reconciliate и решить обе эти проблемы?
Вы на самом деле поднимаете несколько вопросов, поэтому ответ не будет простым и точно не будет полным. Прежде всего, этот вопрос, возможно, лучше задать на crypto.stackexchange.com.
Несколько основных моментов, которые я хотел бы отметить:
- В зависимости от размера данных, вы, скорее всего, не хотите использовать ключи RSA для их шифрования. Ключи RSA могут шифровать только данные размером до определенного размера, который чуть меньше длины ключа. Это тоже не особенно эффективно.
- Я не думаю, что вы хотите получать ключи RSA из пароля пользователя. Это не то, для чего был создан RSA, и нет стандартного решения для этого. Ключ RSA – это просто набор больших целых чисел, но с определенными важными характеристиками. Вместо этого я бы предложил шифровать данные с помощью симметричного ключа, полученного из пароля пользователя с использованием одной из функций *KDF*. Вы можете затем использовать статические, долгосрочные ключи RSA для шифрования симметричных ключей – как основных, так и производных.
- Что касается производной по паролю, я считаю, что это больше связано с управлением жизненным циклом ключей, чем с чем-либо другим. Одно решение заключается в хранении идентификатора ключа для текущих и предыдущих производных ключей в метаданных данных, с которыми они были зашифрованы. Предполагая, что пароль меняется, это изменит идентификатор текущего симметричного ключа (а также сам ключ), но ранее полученные симметричные ключи могут использоваться для расшифровки данных, зашифрованных в прошлом. Все это предполагает, что ключи эффективно обновляются при смене пароля, но никогда не истекают.
Извините, что мой ответ не так полон, как мне хотелось бы. Тем не менее, я думаю, что было бы хорошо сделать шаг назад и переосмыслить исходные требования, поскольку то, что вы описали в своем вопросе, больше похоже на частичное решение. Если сообщество узнает о первоначальной проблеме, они могут быть лучше подготовлены, чтобы предоставить наиболее полный ответ.
Ответ на это будет зависеть от того, от кого вы защищаете данные. Например, если вы доверяете себе, чтобы не стать злым или не быть под давлением.
С шифрованием существует множество подводных камней, и дьявол кроется в деталях.
Если вы доверяете себе, то можете использовать стандартную схему аутентификации/пароля (например, встроенную в ASP.NET Core или scrypt, задайте другой вопрос для этого) и затем использовать шифрование базы данных. Я работал над системами с данными на государственном уровне с аналогичной схемой.
Если вы не доверяете себе, то это становится гораздо сложнее. Однако невозможно создать разумную систему.
Я предлагаю вам ознакомиться с тем, как приложение Signal управляет своей безопасностью.
Также ознакомьтесь с тем, как работают Windows DPAPI и полное шифрование диска Windows с меняющимися паролями.
Тем не менее, несколько мыслей:
Если у вас будут данные, зашифрованные по паролю пользователя, не получайте ключ из пароля. Сгенерируйте случайный ключ (безопасно) и используйте его для шифрования (например, с использованием AES) данных. Затем зашифруйте ключ с помощью надежной хэш-функции (например, bcrypt) из имени пользователя и пароля. Также храните номер версии схемы наряду со всем этим, чтобы вы могли запускать 2 или более схем бок о бок, пока вы переходите от одной схемы к другой. Это все означает, что вы можете изменить пароль, не пересшифровывая данные (только зашифрованный ключ) или пересшифровав данные без изменения пароля (например, если будет обнаружена уязвимость в AES). Также это защищает от атак радужных таблиц на зашифрованный ключ.
Сервисы, такие как Azure Blob Storage, имеют аналогичные встроенные схемы шифрования, которые могут решить некоторые из этих проблем за вас.
Наконец, помните, что криптография сложна и очень легко упустить одну деталь, из-за которой все может развалиться. Так что будьте осторожны, получайте помощь, получайте удовольствие и удачи!
Ответ или решение
Разработка криптографической системы с возможностью совместного использования данных: подход на основе RSA и AES
В вашем вопросе поднимается несколько важных тем, касающихся разработки безопасной системы шифрования для приложения на C#. Мы рассмотрим, как разработать такую систему, учитывая требования к защите данных и возможности их совместного использования.
Проблема 1: Произведение пары ключей RSA из пароля пользователя
Предлагаемое вами решение использовать пароль пользователя для генерации пары ключей RSA – это не стандартный и зачастую небезопасный подход. Вместо этого я рекомендую следующее:
-
Генерация ключей RSA: Создайте пару ключей RSA (открытый и закрытый) один раз при регистрации пользователя. Эти ключи должны быть достаточно длинными (например, 2048 или 4096 бит), чтобы обеспечить надежный уровень безопасности.
-
Шифрование закрытого ключа: Используйте надежный алгоритм симметричного шифрования, такой как AES, чтобы зашифровать закрытый ключ RSA. Ключ шифрования AES может быть получен с использованием функции деривации ключа (KDF, например, PBKDF2 или Argon2), основанной на пароле пользователя.
Проблема 2: Изменение пароля и доступ к ранее зашифрованным данным
Действительно, если пользователь сбросит свой пароль, он потеряет доступ к данным, зашифрованным ранее. Вот несколько стратегий по решению этой проблемы:
-
Использование ключа шифрования для сессии: При шифровании данных используйте симметричный ключ, который будет долгоэкспирирующим. Этот ключ (сессионный ключ) может быть зашифрован с помощью открытого ключа RSA получателя. Так, передача данных другим пользователям может осуществляться через шифрование с использованием их открытых ключей. Это позволяет пользователям делиться данными без необходимости делиться своими паролями.
-
Хранение версии ключа: Храните метаданные о версии ключа для зашифрованных данных. Это позволит вам отслеживать, какой ключ использовался для шифрования определенных данных, и поддерживать возможность расшифровки данных при изменении пароля. Когда пользователь меняет пароль, создайте новый симметричный ключ, но оставьте старый ключ в метаданных для доступа к более ранним данным.
-
Регулярная ротация ключей: Систематическая ротация ключей шифрования позволит снизить риски, возникающие из-за компрометации шифров. При каждом изменении пароля создавайте новый симметричный ключ и шифруйте его с помощью открытого ключа.
Практическое внедрение
Для реализации предложенной архитектуры в C# .NET можно использовать следующие библиотеки:
- System.Security.Cryptography: для генерации пар ключей RSA и использования алгоритмов симметричного шифрования (AES).
- Rfc2898DeriveBytes: для реализации алгоритма PBKDF2, который поможет создать надежный ключ шифрования на основе пароля пользователя.
- BouncyCastle: для дополнительных криптографических функций и алгоритмов, если требуется больше гибкости.
Заключение
Создание безопасной криптографической системы с возможностью совместного использования данных – это сложная задача, но с применением описанных выше принципов и технологий, вы сможете реализовать надежную систему шифрования для вашего приложения на C#. Следуя этим рекомендациям, вы обеспечите безопасность пользовательских данных и возможность их совместного использования без риска компрометации.
Важно помнить, что криптография – это сложная область, и документирование всех решений, используемых вами, а также применение методов тестирования на проникновение и рутинных аудитов безопасности, помогут поддерживать высокий уровень защиты информации.