Если вы хотите использовать программу, не читая блог, загрузите приложение из моего репозитория 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.



Шифрование/дешифрование файлов 📄

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