Удобное шифрование с удостоверяющим центром
Привет всем читателям Хабра! В этой статье рассмотрим проблемы и способы использования шифрования в документообороте. Перед командой стояла задача разработки новой архитектуры шифрования, которую нужно внедрить в наш продукт.
Наш продукт
В двух словах как работает наш продукт - это система контроля и аудита прав доступа к электронным документам. Внутри закрытого контура разворачивается сервер, который распределяет права доступа и отслеживает действия с документами. На каждую пользовательскую машину разворачивается клиентская часть продукта - минифильтр, службы и расширения. При работе с электронными документами наша система принудительно проставляет скрытые (и не только) метки конфиденциальности. Каждый пользователь получает от сервера список доступных меток. При запросе доступа к документу вначале происходит проверка наличия проставленной на нем метки в полученном ранее списке. На документы с более важной информацией выставляются метки с необходимостью шифрования данных.
Первая реализация шифрования
Изначально, когда разрабатывался продукт, для шифрования использовался симметричный алгоритм AES с использованием ключа. Данный способ не является удобным, так как нужно было всегда вводить ключ для получения доступа к документу. Также все сотрудники, которые имеют доступ к данным, должны были знать ключ для расшифровки. Такая реализация выглядит не очень удобно и небезопасно. Скорее всего, для простоты пользователи начнут использовать один ключ для шифрования всех документов, а чтобы не забыть - напишут его на стикере и приклеят к монитору. Жестко регулировать действия пользователя в данном случае - не лучшее решение. Можно заставить пользователей для каждого документа использовать уникальный ключ и с течением времени его изменять. Но тогда, при передаче документа другому пользователю, нужно будет передавать и ключ, и, наверняка, кто-нибудь при очередном обновлении ключа забудет его, и доступ к данным будет утерян. Разработка новой архитектуры, в которой учитывались бы все минусы предыдущей, напрашивалась сама собой.
Новая архитектура
Главной задачей для новой архитектуры был отказ от необходимости ввода ключа при расшифровке данных пользователями. Также имело место "пожелание" шифровать, используя сертификаты в новой версии от управляющего центра. Применение сертификата для шифрования означало использование ассиметричных алгоритмов с открытым и закрытым ключом, что немного не соответствовало концепции нашего продукта. Симметричный алгоритм позволяет расшифровывать данные любым пользователем, который знает ключ. Ассиметричный алгоритм же, в свою очередь, подразумевает, что расшифровать данные может только пользователь, имеющий парный закрытый ключ открытому ключу, которым, собственно, и были зашифрованы данные, что в корне нам не подходило. Документ, зашифрованный одним пользователем, должен был быть беспрепятственно расшифрован другим пользователем, который имел бы право доступа к данным.
Вы предложите решение, Вы же эксперт...
Асимметричное шифрование - это алгоритм шифрования данных, который использует уникальную пару ключей (открытый и закрытый) таким образом, что для каждого открытого ключа есть лишь один закрытый. В нашей системе, где есть сервер и множество пользователей, такая пара ключей будет у каждого. Закрытые ключи хранятся у пользователей в специальных криптоконтейнерах, к которым привязан сертификат. Сертификат, помимо всего прочего, содержит открытый ключ, и передача сертификата как раз означает передачу открытого ключа. Переходя к нашей системе - использование данного алгоритма означает, что сертификаты каждого пользователя (а точнее их открытые ключи) необходимо передать всем пользователям между собой (что уже кажется не самой лучшей идеей, если, например, таких пользователей тысячи и разбросаны они по разным концам света). Самое главное противоречие, с которым мы столкнулись - это как зашифровать данные так, чтобы расшифровать их мог любой пользователь, имеющий определенный доступ. Например, чтобы расшифровать документ пользователю X, нужно, чтобы этот документ был зашифрован именно его открытым ключом. Были озвучены самые разные варианты, порой даже безумные - можно шифровать документ в нескольких экземплярах для каждого пользователя ("самая лучшая идея", исходя из того, что пользователей может быть много).
Все размышления по этому поводу сводились к тому, что если мы не можем знать, кто будет расшифровывать документ, то и непонятно, каким открытым ключом его шифровать. Одна из первых идей, которая пришла в голову - это использовать сервер в нашей архитектуре. Каждый клиент при установке нашего продукта регистрируется на сервере, таким образом может получить от сервера его сертификат (открытый ключ) и все документы шифровать им. Далее, после передачи документа другому пользователю, тот для расшифровки документа должен был бы отправить документ на сервер, который его расшифрует и передаст обратно пользователю. Помимо основного недостатка, а именно, передача документа в открытом виде, данная схема имеет ряд других критичных недостатков, главный из которых - передача документа по сети. Документы бывают разные, и двойная передача его на сервер, а потом обратно - не самая удачная мысль. И второе - а что будет, если сервер вдруг упадет или будет недоступен, доступ к данным будет невозможен. Если первый недостаток можно было обойти, например, шифруя документ открытым ключом пользователя, который запрашивает расшифровку, то два других недостатка избежать невозможно.
Эврика!
Внимательное изучение протокола HTTPS (или SSL) показывает интересную особенность, а именно то, что хоть протокол и использует асимметричное шифрование, на самом деле применяется этот алгоритм лишь для: "выработки сессионного ключа, который, в свою очередь, используется более быстрыми алгоритмами симметричной криптографии для шифрования большого объёма данных" (https://ru.wikipedia.org/wiki/SSL). Таким образом можно сказать, что 2 клиента между собой при помощи асимметричного алгоритма шифрования договариваются о симметричном ключе, который и используют в дальнейшем для симметричного шифрования данных при передаче. Это сделано для увеличения скорости работы, ведь асимметричные алгоритмы работают намного медленнее, чем симметричные. Сама идея генерации симметричного ключа в нашем случае может выглядеть так, что данные будут шифроваться все также, используя симметричное шифрование одним ключом, который сгенерирован сервером. А вот сам ключ нужно будет зашифровать, используя асимметричный алгоритм и открытый ключ пользователя, которому затем и передаст сервер этот зашифрованный ключ (каждому свой соответственно). Пользователи, получив от сервера зашифрованный ключ, расшифровывают его своим закрытым ключом и используют его для расшифровки и зашифровки данных. Таким образом мы получаем, что ключ, который используется в симметричном шифровании данных, вводит не сам пользователь, а его генерирует сервер, для каждой метки свой уникальный. Далее пользователь при запросе сервера о доступных ему метках получает вместе с ними и зашифрованный ключ. Также была решена и проблема с недоступностью сервера, все полученные данные от сервера хранятся у пользователей, в том числе и зашифрованный ключ. Соответственно, при отсутствии связи с сервером, ключ берется из хранилища, и, тем самым, доступ к зашифрованным данным не пропадает.
Итог
Итак, в итоге получается следующее - каждый пользователь должен получить сертификат с закрытым ключом на свою машину. Далее при регистрации пользователь должен отправить полученный сертификат на сервер, который в свою очередь сохранит его в хранилище сертификатов, а у себя в таблице с информацией о пользователях отметит id полученного сертификата. Затем, при создании метки конфиденциальности, которая будет использовать механизм шифрования, сервер должен сгенерировать уникальный ключ, которым непосредственно и будут шифроваться данные. Пользователь, в свою очередь, после регистрации должен запросить от сервера список доступных ему меток, а сервер при отправке списка должен будет взять id сертификата из таблицы, найти в хранилище сам сертификат и использовать его для того, чтобы зашифровать сгенерированный ранее ключ и отправить пользователю. После получения списка пользователю необходимо сохранить его в памяти для дальнейшего использования. Далее во время работы с документом пользователю при проставлении метки, которая использует соответствующий алгоритм шифрования, сначала необходимо взять зашифрованный ключ, расшифровать его своим закрытым ключом и после этого уже зашифровать данные полученным расшифрованным ключом.
P.S.
Надеюсь этот пост кому-нибудь пригодится и поможет сэкономить время.