Если вы хотите использовать программу, не читая блог, загрузите приложение из моего репозитория GitHub ниже и приступайте к работе.
Быстрая демонстрация 🎥
Что такое Электрон 🌌
Электрон – это платформа, позволяющая создавать настольные приложения с использованием таких веб-технологий, как HTML, CSS и JavaScript. Основным преимуществом Electron является кросс-платформенная поддержка, означающая, что одна база кода может использоваться для разных операционных систем, например Windows, macOS и Линукс.
Почему приложение для шифрования файлов 🤔
Я использую VeraCrypt, бесплатное программное обеспечение для шифрования дисков с открытым исходным кодом. Но он может показаться немного сложным и сложным в использовании с его пользовательским интерфейсом и всеми различными настройками, которые он предлагает. Иногда вам просто нужен быстрый способ зашифровать ваши файлы.
Случай использования, который у меня был лично, заключался в том, что петли моего ноутбука нужно было отремонтировать. Поэтому я пошел в сервисный центр, и мне сказали, что я должен оставить свой ноутбук на несколько дней и должен дать им пароль от моего ноутбука, чтобы они могли проверить, все ли работает нормально после того, как ремонт был сделан. Поэтому мне пришлось скопировать все мои личные/семейные фотографии и видео, карты Aadhaar, PDF-файлы карт Pan и т. д., которые всегда нужны для какой-то работы, на мой телефон, потому что я никому не доверяю 😂. Мое приложение действительно помогает в таких ситуациях, и мне не нужно беспокоиться о том, что люди будут просматривать мои вещи.
Другая причина заключается в том, что в macOS уже есть встроенное приложение под названием FileVault, которое можно использовать для шифрования файлов. С другой стороны, в Windows есть BitLocker, но он доступен только для версий Windows 10 Pro, Enterprise или Education.
Видеоурок от Traversy Media 📺
В этом уроке он создает приложение для изменения размера Simple Image и рассказывает, как работает Electron.
Создание приложения Electron 👷♂️
Документация Electron — отличный источник информации о том, как создать приложение Electron и как оно работает. В этом блоге я расскажу о том, как работает процесс шифрования и как я создал приложение. Чтобы узнать больше о том, как работает Electron, посмотрите приведенное выше видео или любой другой учебник на YT.
Сначала создайте файл package.json
с npm init -y
, а затем установите Electron и Tailwind CSS с помощью приведенной ниже команды.
npm i --save-dev electron tailwindcss
package.json
будет выглядеть примерно так.
{ "name": "file-encryptor", "version": "1.0.0", "description": "An app to encrypt/decrypt files with a password.", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "electron": "^21.2.3", "tailwindcss": "^3.2.3" } }
В Electron есть 2 типа процессов: основной процесс (только 1) и процессы визуализации (более 1). Точкой входа приложения Electron является его основной процесс, который управляет состоянием приложения, в то время как процесс Renderer управляет пользовательским интерфейсом с помощью HTML, CSS и JavaScript. Теперь давайте создадим наш main.js
const path = require("path"); const { app, BrowserWindow, Menu } = require("electron"); const { menuTemplate } = require("./menuTemplate.js"); const isMac = process.platform === "darwin"; const isDev = false; // Set to true for DevTools function createMainWindow() { const mainWindow = new BrowserWindow({ title: "File encryptor", width: 420, height: 690, resizable: isDev ? true : false , icon: path.join(__dirname, "./icons/app-icon.ico"), webPreferences: { contextIsolation: true, nodeIntegration: true, preload: path.join(__dirname, "./preload.js"), }, }); if (isDev) { mainWindow.webContents.openDevTools(); } mainWindow.loadFile(path.join(__dirname, "../src/index.html")); } app.whenReady().then(() => { createMainWindow(); const menu = Menu.buildFromTemplate(menuTemplate); Menu.setApplicationMenu(menu); app.on("activate", () => { if (BrowserWindow.getAllWindows().length === 0) { createMainWindow(); } }); }); app.on("window-all-closed", () => { if (!isMac) { app.quit(); } });
Функция createMainWindow
создаст наше оконное приложение. Мы импортируем модули app
, BrowserWindow
и Menu
из электрона. Когда приложение Electron готово, мы создаем новый BrowserWindow с предоставленными свойствами и загружаем его в наш файл index.html
, который будет основным пользовательским интерфейсом нашего приложения, поэтому создайте и добавьте некоторый контент в файл index.html
.
По умолчанию доступ к DevTools недоступен, поэтому, если нам нужно отладить наше приложение, мы можем использовать приведенный ниже код, чтобы открыть DevTools.
mainWindow.webContents.openDevTools()
Я установил для него условие, а свойство resize в моем коде вызывает, когда я создаю приложение, я не хочу, чтобы консоль показывалась пользователю.
Создать меню довольно просто, и вы можете сделать это, посмотрев видео выше.
Запуск приложения 🏃♂️
Базовое приложение завершено, и теперь, чтобы запустить приложение, добавьте приведенную ниже команду в раздел сценариев package.json
и запустите npm run start
.
"start": "electron ."
Это хорошо, но теперь, если мы вносим какие-либо изменения в наше приложение, нам нужно запускать команду снова и снова, чтобы решить эту проблему, мы можем использовать electronmon
, который похож на живой сервер, который мгновенно обновляет любые изменения, которые мы вносим в приложение. Чтобы использовать, введите приведенную ниже команду.
npx electronmon .
Настройка Tailwind CSS 🎨
Лучший видеоурок по настройке TailwindCSS от Shruti Balasa
Шифрование 🔐
const nodeCrypto = require("crypto"); const { Buffer } = require("buffer"); const app = { encrypt: function (data, password) { const salt = nodeCrypto.randomBytes(16); const key = nodeCrypto.pbkdf2Sync(password, salt, 100000, 32, "sha256"); const cipher = nodeCrypto.createCipheriv("aes-256-gcm", key, salt); const encryptedData = Buffer.concat([cipher.update(data), cipher.final()]); const authTag = cipher.getAuthTag(); return Buffer.concat([salt, authTag, encryptedData]); }, decrypt: function (data, password) { try { const salt = data.subarray(0, 16); const authTag = data.subarray(16, 32); const encData = data.subarray(32, data.length); const key = nodeCrypto.pbkdf2Sync(password, salt, 100000, 32, "sha256"); const decipher = nodeCrypto.createDecipheriv("aes-256-gcm", key, salt); decipher.setAuthTag(authTag); const plainText = Buffer.concat([ decipher.update(encData), decipher.final(), ]); return plainText; } catch (e) { return true; } }, }; module.exports.app = app;
Теперь давайте пройдемся по коду шифрования. Здесь я использовал встроенный модуль Крипто в NodeJS. Обе функции шифрования и дешифрования принимают data
, то есть содержимое файла, и password
для шифрования/дешифрования. Затем, используя pbkdf2Sync
(Синхронизация означает синхронный), который является KDF, мы создаем безопасный ключ из пароля и используем его для создания Cipheriv
и Decipheriv
, которые используют AES-256
для шифрования/дешифрования данных. Я также добавляю случайную соль и AuthTag в начале зашифрованных данных, поскольку они понадобятся нам для расшифровки данных.
Что такое KDF и AuthTag 🔑
KDF (Key Derivation Functions) извлекает ключ из значения. При сохранении пароля пользователя в базе данных для сохранения пароля используются SHA-256
или некоторые другие хеш-функции (которые являются односторонними функциями, означающими, что их нельзя вернуть к исходному содержанию). Таким образом, в случае утечки данных хакеру становятся доступны хэши, а не пароли в открытом виде. Но SHA-256
как KDF не так безопасен. Уязвим к грубой силе и атакам по словарю. PBKDF2 — лучший KDF, предназначенный для противостояния таким атакам.
AuthTag (тег аутентификации), также известный как MAC (код аутентификации сообщения), похож на хеш-функцию, поэтому незначительное изменение данных или ключа приведет к совершенно другому значению AuthTag, из которого мы можем узнать, что данные были подделаны. Вы можете проверить страницу ниже, чтобы узнать больше о MAC и параметрах, используемых в PBKDF2.
KDF: получение ключа из пароля
Время процессора для получения ключа (например, 0,2 секунды) + немного памяти (ОЗУ). Таким образом, получение ключа является «вычислительно затратным, поэтому…cryptobook.nakov.com»
Шифрование/дешифрование файлов 📄
const path = require("path"); const fs = require("fs"); const { pipeline } = require("stream/promises"); const { Transform } = require("stream"); const { app } = require("./encryption.js"); const encHighWaterMark = 1024 * 1024 * 100; async function encryptFile(fileLocation, password) { const fileReadStream = fs.createReadStream(fileLocation, { highWaterMark: encHighWaterMark, }); const filePath = path.parse(fileLocation).dir; const fileName = path.parse(fileLocation).name; const fileExt = path.parse(fileLocation).ext; const newEncFile = filePath + "\\" + fileName + "__ENC" + fileExt; const fileWriteStream = fs.createWriteStream(newEncFile); await pipeline( fileReadStream, new Transform({ transform(chunk, encoding, callback) { const encryptedData = app.encrypt(chunk, password); callback(null, encryptedData); }, }), fileWriteStream ); } module.exports.encryptFile = encryptFile; module.exports.encHighWaterMark = encHighWaterMark;
Для чтения/записи файлов мы будем использовать модули fs
и path
, что упрощает работу с файлами. С помощью fs.createReadStream
и fs.createWriteStream
мы можем читать/записывать файл небольшими порциями вместо всего файла, что может привести к заполнению памяти, если размер файла действительно большой. Размер фрагмента по умолчанию, возвращаемый потоком, равен 64KiB
, который можно увеличить с помощью параметра highWaterMark
.
С помощью модуля path
мы получаем каталог, имя файла и расширение файла, из которого мы можем создать новый путь к файлу и добавить __ENC
к имени файла.
Затем с помощью pipeline
мы можем связать наши ReadStream
и WriteStream
, которые обрабатывают ошибки и закрывают поток, чтобы убедиться в отсутствии утечек памяти. Поток Transform в нем — это поток, который считывает данные из ReadStream
, преобразует данные, а затем записывает преобразованные данные в WriteStream
. Вы можете прочитать больше об этом здесь с более простыми примерами.
Предварительная загрузка скриптов 🔗
Теперь, когда у нас есть все готовые функции, как мы можем использовать их во внешнем интерфейсе нашего приложения, то есть в процессе Renderer, потому что основной процесс — это среда NodeJS, которая имеет полный доступ к ОС и может использовать встроенные модули NodeJS. С другой стороны, процесс Renderer запускает веб-страницы и не запускает NodeJS из соображений безопасности и, следовательно, не может получить доступ к модулям NodeJS, которые мы использовали, например — fs, Crypto и path.
С помощью скрипта Preload мы можем соединить код NodeJS с процессом Renderer и предоставить любую функциональность или API, используя contextBridge
из электрона, а для доступа к нему мы используем объект window
.
const { contextBridge } = require("electron"); const { encryptFile } = require("../backend/encryptFile.js"); const { decryptFile } = require("../backend/decryptFile.js"); const fs = require("fs"); contextBridge.exposeInMainWorld("encryptFile", encryptFile); contextBridge.exposeInMainWorld("decryptFile", decryptFile); contextBridge.exposeInMainWorld("unlinkSync", { unlinkSync: (path) => fs.unlinkSync(path), });
Чтобы узнать больше о Preload Script, ознакомьтесь с документацией здесь или видео ниже для быстрого примера.
Вывод 👋
И это в значительной степени основной код приложения. Остальная часть приложения представляет собой простой HTML, CSS и JavaScript, с которыми вы можете ознакомиться здесь. Это не самый подробный блог об Electron, поэтому я добавил несколько ресурсов для изучения. Я просто хотел дать вам обзор того, как я создал приложение.
Загрузите его по ссылке ниже и попробуйте. Надеюсь, вы что-то узнали, и спасибо за чтение.
Следите за мной в твиттере — https://twitter.com/abhishekY495