Без теории совсем никак, чтобы хотя бы себе объяснить что и как. И её реально будет немного. В основе шифрования лежат всего два алгоритма: RSA и AES.
Сервер генерирует два ключа keyRSA1 (для шифрования данных, обычно называется публичным) и keyRSA2 (для расшифровки данных, обычно называется приватным) и отдает клиенту ключ для шифрования данных keyRSA1 (поэтому ключ и называется публичным что он доступен всем). Этот ключ у нас сохраняется в javascript который загружает себе пользователь при открытии страницы. После загрузке скрипта javascript происходит генерация случайного ключа keyAES и этот ключ не знает никто, кроме самого скрипта на машине пользователя и в идеальном виде этот ключ нигде не сохраняется. При отправке данных на сервер javascript шифрует их методом AES с помощью сгенерированного ключа keyAES. А также шифрует сам ключ методом RSA с помощью публичного ключа keyRSA1. После этого скрипт отправляет на сервер зашифрованный ключ и зашифрованные этим ключом данные. Если кто-то перехватит данные, то он не сможет расшифровать их, так как у него нет ключа. А ключ он не сможет расшифровать так как у него нет приватного ключа с сервера. Сервер получает данные. Расшифровывает ключ keyAES с помощью приватного ключа keyRSA2, а потом расшифровывает данные этим ключом данные. И все. Данные доставлены на сервер. При ответе сервер свой ответ шифрует данные ключом keyAES, который он получил с клиента. И в этом случае его не нужно отправлять обратно пользователю, так как он у пользователя уже имеется.
Список необходимых функций (на схеме эти блоки выделены зеленым). Префикс php - функции на PHP, префикс js - функции на JavaScript
Для реализации на javascript я пользовался следующим источником. Функция ShasoftCrypto на вход принимает следующие параметры
// Значения по умолчанию
var defaultOptions = {
rsaName: "RSA-OAEP", // Имя RSA метода шифрования
rsaKeyPublic: undefined, // Публичный ключ RSA
rsaLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
aesName: "AES-CBC", // Имя AES метода шифрования
aesLength: 256, // can be 128, 192, or 256
ivLength: [16, 16], // Размер инициализирующего вектора для AES шифрования,
hashName: "SHA-1" // Имя хеширования
};
Метод ShasoftCrypto возвращает объект для работы с Shasoft Crypto Api
{
rsa: {
/**
* Зашифровать данные методом RSA
*
* @param {ArrayBuffer} abData - данные для шифрования
* @param {string} [keyRSA1=options.rsaKeyPublic] Публичный (открытый) ключ RSA
* @returns {Promise} Зашифрованные данные в формате ArrayBuffer
*/
encode: function () { }
},
aes: {
/**
* Сгенерировать ключ AES
*
* @returns {Promise} Ключ AES в формате string
*/
generateKey: function () { },
/**
* Зашифровать данные методом AES
*
* @param {ArrayBuffer} abData - данные для шифрования
* @param {string} [keyAES=генерируется] Ключ AES
* @returns {Promise} Зашифрованные данные в формате ArrayBuffer
*/
encode: function () { },
/**
* Расшифровать данные методом AES
*
* @param {ArrayBuffer} abData - данные для расшифровки
* @param {string} [keyAES=генерируется] Ключ AES
* @returns {Promise} Расшифрованные данные в формате ArrayBuffer
*/
decode: function () { }
},
/**
* Преобразовать Array Buffer в строку Base64
*
* @param {ArrayBuffer} abData - данные в формате ArrayBuffer
* @returns {string} строка в кодировке base64
*/
abToB64: function () { },
/**
* Преобразовать строку base64 в Array Buffer
*
* @param {string} str - строка в кодировке base64
* @returns {ArrayBuffer} данные в формате ArrayBuffer
*/
b64ToAb: function () { },
/**
* Преобразовать строку в Array Buffer
*
* @param {string} str - строка
* @returns {ArrayBuffer} данные в формате ArrayBuffer
*/
strToAb: function () { },
/**
* Преобразовать Array Buffer в строку
*
* @param {ArrayBuffer} abData - данные в формате ArrayBuffer
* @returns {string} строка
*/
abToStr: function () { },
/**
* Преобразовать JSON данные в Array Buffer
*
* @param {any} value - данные в формате JSON
* @returns {ArrayBuffer} данные в формате ArrayBuffer
*/
jsonToAb: function () { },
/**
* Преобразовать Array Buffer в JSON
*
* @param {ArrayBuffer} abData - данные в формате ArrayBuffer
* @returns {any} данные в формате JSON
*/
abToJson: function () { },
/**
* Вычислить ХЕШ строки
*
* @param {ArrayBuffer} abData данные для вычисления хеш-а
* @returns {string} хеш данных
*/
hash: function () { }
};
Ключ AES представляет из себя строку в формате Base64.
Для реализации методов на PHP я использовал документацию. Класс PHP в конструктор принимает массив параметров. Параметры по умолчанию
[
'rsaName' => "RSA-OAEP", // Имя RSA метода шифрования
// Метод вызывается для получения публичного ключа RSA если он не указан при вызове метода
'rsaKeyPublic' => function () {return null;},
'rsaLength' => 2048,
'aesName' => "AES-256-CBC", // Имя AES метода шифрования
// Метод вызывается для получения ключа AES если он не указан при вызове метода
'aesKey' => function () {return null;},
'ivLength' => [16, 16], // Размер инициализирующего вектора для AES шифрования
'hashName' => "SHA-1", // Имя хеширования
]
Сам класс содержит следующие методы
class Api
{
// Параметры
protected $options;
// Используемое хранилище
public function __construct($options = []);
// Получить открытый(keyRSA1) и закрытий(keyRSA2) ключи для RSA шифрования
public function generateRSAKeys();
// Расшифровать данные ключом keyRSA2, закодированные методом RSA с помощью ключа keyRSA1
public function decodeRSA($cryptData, $keyRSA2 = null);
// Расшифровать данные AES
public function decodeAES($data, $keyAES = null);
// Зашифровать данные AES
public function encodeAES($data, $aesKey);
// Сгенерировать AES ключ
public function generateAESKey();
}
Для удобства разработки лучше спрятать процесс шифрования/расшифровки обмена сообщениями клиента и сервера за функцией отправки данные на сервер. Алгоритм её работы весьма простой:
Для отправки данных пользуюсь библиотекой axios (документация на русском) (она является стандартом для использование с Vue хотя можно использовать и другую). Достаточно написать функцию отправки, основанную на axios. На серверной части Lumen в котором есть удобный механизм посредников. В этом посреднике я и буду расшифровывать данные, полученные от клиента и шифровать ответ от сервера (при необходимости).
При запуске файлов проекта необходимо открывать их по протоколу https. Если открывать по http, то функции Crypto Web API будут недоступны