Приветствую, почтенное читатели.
Недавно столкнулся с необходимостью шифровать и расшифровывать файлы в Qt проекте. Основное условие – простота и прозрачность, так как, по сути, нужно заменить работу с файлами через QFile на что-то, что может писать данные в зашифрованном виде и читать их из зашифрованного файла. В итоге родился небольшой класс. Затем появилось желание поделиться с общественностью им, может кому-то пригодится и сэкономит время, а может кто-то доработает его и изменит жизнь к лучшему.
Основным моментом, как я уже сказал, было простая и прозрачная работа с классом. Т.е. класс должен уметь писать данные сразу в зашифрованном виде, а также читать исходные данные из зашифрованного файла.
Тот, кто уже знаком с Qt, и в частности с QIODevice, знает, какой это мощный инструмент для работы с устройствами ввода-вывода. Поэтому, практически сразу было принято решение взять этот класс за основу и отнаследоваться от него. Не буду подробно останавливаться на этом моменте, только отмечу, что основная «магия» заключается в переопределении методов
qint64 QIODevice::writeData(const char *data, qint64 len)
и
qint64 QIODevice::readData(char *data, qint64 len)
соответственно для записи данных и их чтения.
Чтобы указать, с каким файлом работать, у пользователя класса есть несколько способов:
1) Передать путь к файлу
2) Передать указатель на объект QFileDevice.
Если вам интересно, почему QFileDevice, а не QFile, то ответ прост. Чтобы можно было передавать такой полезный класс как QSaveFile.
После этого стал актуальным вопрос, какой инструмент использовать непосредственно для шифрования и расшифровывания данных? После недолгого исследования было принято решение остановиться на библиотеке OpenSSL потому что, во-первых, это один из лидеров в мире шифрования данных, во-вторых, наш проект уже использовал эту библиотеку для других задач.
Поэтому при выборе алгоритма шифрования было решено использовать симметричный алгоритм, т.к. он позволяет быстро шифровать данные блоками. Важным требованием к реализуемому классу было возможность писать в файл с любого места. В этой связи пришлось иметь в виду, чтобы зашифрованные данные не зависели от предыдущих или последующих данных. В свете всех ограничений, было решено выбрать алгоритм AES с режимом ECB. Такой режим позволяет получить независимость конкретного блока данных от остальных, однако, уровень защищенности конечно падает. Пришлось смириться с этим. Стоит отметить, что сейчас пользователь может выбрать битность ключа 128, 192 или 256 бит (чем меньше битность, тем быстрее работает алгоритм, но становится при этом менее защищенным). Для шифрования с помощью OpenSSL использовались высокоуровневые EVP методы шифрования.
В ходе реализации класса пришла идея сделать некий заголовок для зашифрованного файла и хранить так некоторые нужные вещи, например, хеш от пароля и соли, чтобы контролировать их правильность при открытии файла. Естественно, в любом случае, с неправильным паролем и солью нормально прочитать файл пользователь не сможет, но мы решили это проконтролировать в момент открытия файла, чтобы пользователь был уверен, если он открыл корректно зашифрованный файл, он будет работать в итоге с правильными исходными данными.
Из интересных моментов еще стоит отметить то, что класс может работать и с обычными файлами. Если не указать пароль, то с файлом будем работать также, как будто работаем с обычным QFile.
Для понимания работы с классом, я сделал два тестовых проекта.
Первый проект содержит выполнение основных операций с файлами. Для сравнения используется объект класс QFile, результаты которого проверяются с разработанным классом.
Второй проект показывает, как можно полезно организовать работы с зашифрованными файлами для конечного пользователя. В проекте реализовано шифрование выбранной пользователем картинки и последующее отображение ее в QWebView – файл зашифрован, но пользователь видит исходное изображение.
Использовать класс очень просто – копируете 2 файла (cryptfiledevice.cpp и cryptfiledevice.h) в ваш проект и можете уже работать с ним.
Зависимости на данный момент:
1) Qt >= 5.1.0 (так как для хеша пароля и соли используется SHA3)
2) Компилятор, поддерживающий c++11
Класс и тестовые проекты тестировались на Windows 7 (x86, x64), Ubuntu (x64) и MacOS X Mavericks.
Есть еще некоторые мысли, которые хотелось бы реализовать в будущем. Это реализация методов remove, rename, exists по аналогии с QFile, чтобы не подключать QFile, если нет в нем необходимости. Также, хотелось бы добавить метод, который позволит пользователю определить, является этот файл зашифрованным или нет. Надеюсь, что в скором будущем руки дойдут, и я добавлю этот функционал.
Плюс ко всему, можно будет подумать насчет работы с другими алгоритмами.
Исходники выложил в открытом доступе на GitHub под лицензией MIT. Берите, пользуйтесь, совершенствуйте на здоровье. Все предложения и пожелания можете отправлять на email или писать в issue tracker (вся информация есть в README на GitHub).
Ссылка на GitHub: CryptFileDevice
Автор: tywonka