Private KeeperPrivate Keeper
Объект PK

Шифрование

Все 11 методов PK для AES, AES-GCM, RSA, Salsa20, ChaCha20 и Blowfish с проверяемыми обратимыми примерами.

Объект PK предоставляет 11 методов шифрования. Для новых данных выбирайте AES-GCM: он шифрует содержимое и проверяет его целостность. AES-CBC, RSA и Blowfish нужны для точного внешнего договора, а Salsa20 и ChaCha20 в текущем интерфейсе не имеют пригодной обратной операции.

Что выбрать

Новые данные

AESGCMEncrypt создаёт начальный вектор и тег подлинности, а AESGCMDecrypt проверяет тег до возврата текста.

Внешний договор AES

AESEncrypt и AESDecrypt нужны для AES-CBC, AES-ECB, ключа PBKDF2 или совместимости с формой CryptoJS.

Короткий секрет получателю

RSAEncrypt шифрует открытым ключом, RSADecrypt расшифровывает закрытым. Длинные данные шифруйте AES-GCM, а RSA передавайте только ключ AES.

Совместимость

Salsa20, ChaCha20 и Blowfish здесь имеют ограничения интерфейса и не подходят как новый общий договор без отдельной причины.

Пароль пользователя не нужно шифровать для последующей расшифровки. Для хранения и проверки паролей предназначены парольные функции объекта PK.

Общие границы

Ключ хранится отдельно от шифротекста

Base64 и шестнадцатеричная запись не скрывают ключ. Не помещайте ключ в один файл, запрос, журнал или объект хранения вместе с зашифрованными данными. Начальный вектор и соль можно хранить рядом, но без них последующая расшифровка невозможна.

Все сложные методы принимают строку JSON, а не готовый объект JavaScript. Создавайте её через JSON.stringify({...}): так кавычки, обратные косые черты и переводы строк внутри сообщения или пароля будут оформлены правильно. Размеры ключа и вектора считаются в байтах после декодирования выбранного представления, а не в видимых знаках.

AES-CBC и AES-ECB

PK.AESEncrypt(input)

Шифрует text заданным ключом и возвращает шифротекст либо строку JSON с шифротекстом и данными, необходимыми для обратной операции.

PK.AESDecrypt(input)

Принимает шифротекст либо сохранённый пакет, требует тот же ключ и параметры и возвращает открытые байты в выбранном представлении.

text

Объект с value и encoding. Для шифрования: utf8, plaintext, hex, hexa, base64; для расшифрования также json и cryptojs-json.

key

source: "raw" принимает значение и его представление. source: "password" дополнительно требует параметры получения ключа.

cipher

mode: CBC или ECB; padding: PKCS7; keyBits: 128, 192 или 256; объект iv обязателен.

output

encoding задаёт представление результата, envelopenone, json или совместимую форму CryptoJS.

Для CBC вектор всегда содержит 16 байт. Сырой ключ после декодирования должен содержать ровно 16, 24 или 32 байта согласно keyBits. Для ECB объект iv всё равно обязателен и должен быть ровно {"source":"none"}.

Проверяемая пара AES-CBC

const sourceText = "hello";
const keyHex = "000102030405060708090a0b0c0d0e0f";

const encryptedPackage = PK.AESEncrypt(JSON.stringify({
  text: { value: sourceText, encoding: "utf8" },
  key: { source: "raw", value: keyHex, encoding: "hex" },
  cipher: {
    mode: "CBC",
    padding: "PKCS7",
    keyBits: 128,
    iv: { source: "random" },
  },
  output: { encoding: "base64", envelope: "json" },
}));

const decryptedText = PK.AESDecrypt(JSON.stringify({
  text: { value: encryptedPackage, encoding: "json" },
  key: { source: "raw", value: keyHex, encoding: "hex" },
  cipher: {
    mode: "CBC",
    padding: "PKCS7",
    keyBits: 128,
    iv: { source: "envelope" },
  },
  output: { encoding: "utf8", envelope: "none" },
}));

if (decryptedText !== sourceText) {
  throw new Error("AES не восстановил исходный текст");
}

encryptedPackage содержит строку вида {"ct":"...","iv":"..."}. Вектор не является секретом, но должен сохраниться для расшифрования. Случайный вектор нельзя использовать с envelope: "none": исполнитель не разрешит потерять обязательные данные.

AES-CBC и AES-ECB не подтверждают целостность

Изменённый шифротекст может пройти расшифрование без явного признака подмены, а ECB дополнительно раскрывает повторяющиеся блоки. Если внешний договор не требует эти режимы, используйте AES-GCM.

Строгий объект, ключ из пароля, соль PBKDF2 и совместимость CryptoJS подробно разобраны по отдельности: AESENCRYPT и AESDECRYPT.

AES-GCM

AES-GCM создаёт случайный начальный вектор и тег подлинности. Тег проверяет шифротекст и дополнительные подтверждаемые данные associatedData; изменение любого из них должно завершить расшифрование ошибкой.

PK.AESGCMEncrypt(input)

Принимает текст UTF-8 и ключ в шестнадцатеричной форме. Возвращает строку JSON с полями iv, tag, encryptedText, key и outputFormat либо объектом ошибки.

PK.AESGCMDecrypt(input)

Требует encryptedText, iv, tag и тот же ключ. Возвращает исходный текст только после успешной проверки тега; ошибка также возвращается строкой JSON с полем error.

Ключ

key — шестнадцатеричная строка; keyLength равен 128, 192 или 256, по умолчанию 256. Длина ключа должна точно совпасть.

Представление

outputFormat: hex, base64, base64url или base32. Одно значение применяется к шифротексту, вектору и тегу.

Связанные данные

associatedData необязательно и всегда передаётся в шестнадцатеричной записи. Оно не шифруется, но должно побайтно совпасть при обратной операции.

Проверяемая пара AES-GCM

const sourceText = "hello";
const keyHex =
  "000102030405060708090a0b0c0d0e0f" +
  "101112131415161718191a1b1c1d1e1f";
const associatedDataHex = "686561646572";

const encryptedResult = JSON.parse(PK.AESGCMEncrypt(JSON.stringify({
  text: sourceText,
  key: keyHex,
  associatedData: associatedDataHex,
  keyLength: 256,
  outputFormat: "base64",
})));

if ("error" in encryptedResult) {
  throw new Error(encryptedResult.error);
}

const encryptedPackage = {
  encryptedText: encryptedResult.encryptedText,
  iv: encryptedResult.iv,
  tag: encryptedResult.tag,
  outputFormat: encryptedResult.outputFormat,
};

const decryptedText = PK.AESGCMDecrypt(JSON.stringify({
  encryptedText: encryptedPackage.encryptedText,
  iv: encryptedPackage.iv,
  tag: encryptedPackage.tag,
  key: keyHex,
  associatedData: associatedDataHex,
  keyLength: 256,
  outputFormat: encryptedPackage.outputFormat,
}));

if (decryptedText !== sourceText) {
  throw new Error("AES-GCM не восстановил исходный текст");
}

Не сохраняйте весь результат шифрования

AESGCMEncrypt повторяет входной секрет в поле key. В примере создаётся новый encryptedPackage только из encryptedText, iv, tag и outputFormat. Именно этот сокращённый объект можно хранить рядом с шифротекстом; поле key нужно отбросить.

При успехе AESGCMDecrypt возвращает открытый текст напрямую, а при ошибке — строку JSON вида {"error":"..."}. Поэтому пример проверяет точное восстановление известного исходного текста, а не пытается разбирать любой результат как JSON. Поля tagSizeBits у метода нет: старое описание обещало его ошибочно, а исполнитель не читает такое поле. Точные договоры приведены в AESGCMENCRYPT и AESGCMDECRYPT.

RSA

PK.RSAKeyGen(input)

Генерирует пару RSA. Параметр — строка с целым числом битов, например "2048", а не объект JSON. Пустая или нечисловая строка молча выбирает 2048; библиотека принимает размеры от 512 до 8192 бит.

const keys = JSON.parse(PK.RSAKeyGen("2048"));

Успешный объект содержит:

  • privateKeyPem — незашифрованный закрытый ключ PKCS#1;
  • publicKeyPem — открытый ключ PEM;
  • privateKey — составные части закрытого ключа в Base64;
  • publicKey.modulus и publicKey.exponent — открытые части в Base64;
  • keySize — размер в битах.

Результат содержит закрытый ключ

Не выводите весь объект keys в журнал и не передавайте его получателю открытого ключа. Поле privateKeyPem позволяет расшифровать все данные этой пары.

В JavaScript публичное имя действительно равно RSAKeyGen. Соответствующая функция in-Line называется RSAGENKEY; старое имя RSAKEYGEN там не существует. Состав результата и границы размера приведены в справке по RSAGENKEY.

PK.RSAEncrypt(input)

Шифрует короткую строку открытым ключом и возвращает шифротекст Base64. Обязательны text, modulus и publicExponent; modulus принимает открытый ключ PEM либо модуль в шестнадцатеричной записи, а экспонента всегда задаётся так же.

PK.RSADecrypt(input)

Расшифровывает Base64 соответствующим закрытым ключом. Обязательны encryptedText и privateKeyPem; поле закрытого ключа принимает PEM либо запись DER PKCS#1 в шестнадцатеричной форме.

Заполнение

encType: oaep по умолчанию или v1.5. Для OAEP явно задавайте одинаковые oaepHash и oaepMgfHash с обеих сторон.

Метка и порядок

oaepLabel — необязательная шестнадцатеричная строка; littleEndian по умолчанию false. Оба значения должны совпасть.

Размер сообщения

Для RSA-2048 с OAEP SHA-256 предел равен 190 байтам. Более длинные данные шифруйте AES-GCM.

Проверяемая пара RSA

const sourceText = "session-key-001";
const keys = JSON.parse(PK.RSAKeyGen("2048"));

if (keys.publicKey.exponent !== "AQAB") {
  throw new Error("Пример ожидает открытую экспоненту RSA 65537");
}

const encryptedText = PK.RSAEncrypt(JSON.stringify({
  text: sourceText,
  modulus: keys.publicKeyPem,
  publicExponent: "010001",
  encType: "oaep",
  oaepHash: "sha256",
  oaepMgfHash: "sha256",
  littleEndian: false,
}));

const decryptedText = PK.RSADecrypt(JSON.stringify({
  encryptedText,
  privateKeyPem: keys.privateKeyPem,
  encType: "oaep",
  oaepHash: "sha256",
  oaepMgfHash: "sha256",
  littleEndian: false,
}));

if (decryptedText !== sourceText) {
  throw new Error("RSA не восстановил исходный текст");
}

Пример использует открытый PEM и явно подтверждает стандартную экспоненту 65537, потому что ранее описанный PK.base64ToHex сейчас сломан и не должен участвовать в обработке открытых частей. Строка сообщения ограничена ASCII намеренно: исполнитель RSA использует системную кодовую страницу, поэтому произвольный русский текст не даёт переносимого договора.

Точные поля обеих операций приведены в RSAENCRYPT и RSADECRYPT.

Salsa20 и ChaCha20

Оба метода читают key, nonce и message как строки UTF-8 и возвращают шифротекст Base64. Шестнадцатерично выглядящий ключ остаётся текстом: строка 0011 означает четыре байта символов, а не два байта 00 11.

PK.salsa20Encrypt(input)

const encryptedText = PK.salsa20Encrypt(JSON.stringify({
  key: "1234567890abcdef",
  nonce: "12345678",
  message: "hello",
  keyLength: 128,
  rounds: 20,
}));

// encryptedText === "oymXuqc="

Ключ содержит ровно 16 байт UTF-8, одноразовое число — ровно 8. Полный договор приведён в справке по SALSA20ENCRYPT.

Фактический ключ должен содержать ровно 16 или 32 байта, keyLength принимает 128 или 256, а rounds8, 12 или 20. Поле keyLength не сверяется с фактической длиной ключа.

Ограничение обратной операции

Публичной обратной операции нет

Объект PK не предоставляет salsa20Decrypt или chacha20Decrypt. Повторный вызов шифрования также не является надёжной заменой: обёртка читает сообщение как UTF-8, а шифротекст возвращает в Base64. Для новой обратимой схемы используйте AES-GCM.

Никогда не повторяйте одну пару ключа и nonce для двух сообщений. Эти методы не создают одноразовое число и не добавляют тег подлинности, поэтому сами не защищают от подмены.

Blowfish

Blowfish оставлен для совместимости. Его блок равен 8 байтам, а встроенного тега подлинности нет, поэтому для новых данных выбирайте AES-GCM.

PK.blowfishEncrypt(input)

Шифрует text с заданными ключом, режимом, заполнением и начальным вектором.

PK.blowfishDecrypt(input)

Требует те же параметры и возвращает открытые байты в выбранном представлении.

Ключ

От 32 до 448 бит с шагом 8. Для обратимой пары используйте текстовый ключ ровно keyLength / 8 байт.

Режим

ECB, CBC, CFB, CTR, CTS, OFB или SIC; для обычного текста задавайте одинаковое PKCS7 с обеих сторон.

Представление

Вход и выход: plaintext, hexa или base64. Значения по умолчанию шифрования и расшифрования не образуют пару.

Проверяемая пара Blowfish-CBC

const sourceText = "hello";
const keyText = "0123456789abcdef";
const ivHex = "0001020304050607";

const encryptedText = PK.blowfishEncrypt(JSON.stringify({
  text: sourceText,
  key: keyText,
  mode: "CBC",
  padding: "PKCS7",
  keyLength: 128,
  blockSize: 64,
  iv: ivHex,
  includeIV: false,
  inputFormat: "plaintext",
  outputFormat: "base64",
}));

const decryptedText = PK.blowfishDecrypt(JSON.stringify({
  text: encryptedText,
  key: keyText,
  mode: "CBC",
  padding: "PKCS7",
  keyLength: 128,
  blockSize: 64,
  iv: ivHex,
  includeIV: false,
  inputFormat: "base64",
  outputFormat: "plaintext",
}));

if (decryptedText !== sourceText) {
  throw new Error("Blowfish не восстановил исходный текст");
}

Не полагайтесь на includeIV

Текущая ветвь автоматического добавления случайного вектора формирует длину результата неверно. Для обратимой операции используйте includeIV: false, задавайте ровно 8 байт шестнадцатеричной строкой и передавайте то же значение при расшифровании.

Подробные поля и ограничения обеих операций приведены в BLOWFISHENCRYPT и BLOWFISHDECRYPT.