nacl.box.keyPair: генерация пары ключей

Функция nacl.box.keyPair() в TweetNaCl.js / nacl.js предназначена для генерации асимметрической пары ключей, используемой в криптосистеме публичного шифрования Curve25519. Результатом её работы является связка из двух значений: публичного ключа и приватного ключа, которые далее применяются в операциях nacl.box для шифрования и расшифрования сообщений.


Вызов функции имеет следующий вид:

const keyPair = nacl.box.keyPair();

Возвращаемый объект содержит два поля:

{
  publicKey: Uint8Array(32),
  secretKey: Uint8Array(32)
}

Обе части представляют собой массивы байт фиксированной длины 32 байта.

publicKey

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

secretKey

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


Алгоритмическая основа

nacl.box.keyPair() основана на криптографическом примитиве Curve25519 (X25519), предназначенном для обмена ключами и построения защищённых каналов связи.

Генерация ключевой пары включает:

  • создание криптографически стойкого случайного 32-байтового значения
  • преобразование его в приватный ключ
  • вычисление публичного ключа через эллиптическую кривую Curve25519

Механизм генерации случайности

В браузерной среде библиотека использует:

crypto.getRandomValues()

Это источник криптографически стойкой случайности, предоставляемый Web Crypto API.

В среде Node.js (в зависимости от сборки) используется:

  • crypto.randomBytes() или
  • полифилл, реализующий аналогичную функциональность

Качество случайности критично: повторяемость или предсказуемость приводит к компрометации всей криптосистемы.


Пример генерации ключевой пары

import nacl from "tweetnacl";

const keyPair = nacl.box.keyPair();

console.log(keyPair.publicKey);
console.log(keyPair.secretKey);

Каждое выполнение этого кода создаёт новую независимую пару ключей.


Использование ключей в nacl.box

Сгенерированная пара напрямую используется в асимметричном шифровании:

const message = new TextEncoder().encode("секретное сообщение");

const nonce = nacl.randomBytes(24);

const encrypted = nacl.box(
  message,
  nonce,
  recipientPublicKey,
  senderSecretKey
);

Расшифрование:

const decrypted = nacl.box.open(
  encrypted,
  nonce,
  senderPublicKey,
  recipientSecretKey
);

Особенности формата Uint8Array

Все ключи представлены в виде Uint8Array(32), что важно учитывать:

  • это не строка
  • это не hex или base64
  • это бинарное представление

При работе с API или хранении данные часто требуется преобразование.


Сериализация ключей

Для хранения или передачи ключи обычно конвертируются.

В hex

function toHex(bytes) {
  return Array.from(bytes)
    .map(b => b.toString(16).padStart(2, "0"))
    .join("");
}

В base64

function toBase64(bytes) {
  return btoa(String.fromCharCode(...bytes));
}

Обратное преобразование требуется при восстановлении ключей.


Важность разделения ключей

Архитектура публичного шифрования строго разделяет роли:

  • публичный ключ можно безопасно публиковать
  • приватный ключ никогда не должен покидать контролируемую среду

Любая утечка secretKey полностью компрометирует безопасность сообщений.


Повторная генерация и идентичность

Каждый вызов nacl.box.keyPair():

  • создаёт новую пару
  • не зависит от предыдущих вызовов
  • не воспроизводим (если не используется deterministic seed версия API)

Это означает отсутствие возможности восстановить ключ без сохранённого secretKey.


Связь с seed-генерацией

В TweetNaCl существует альтернативная функция:

nacl.box.keyPair.fromSecretKey(secretKey)

Она позволяет восстановить публичный ключ из приватного:

const kp = nacl.box.keyPair();

const restored = nacl.box.keyPair.fromSecretKey(kp.secretKey);

restored.publicKey всегда совпадает с исходным.


Хранение ключей

При проектировании систем хранения необходимо учитывать:

  • приватный ключ нельзя хранить в открытом виде
  • предпочтительно шифрование на уровне устройства
  • использование secure storage (Keychain, Keystore, Web Crypto storage)

Пример небезопасного подхода:

localStorage.setItem("secretKey", toBase64(keyPair.secretKey));

Такой способ делает ключ уязвимым для XSS-атак.


Частые ошибки при использовании

Использование publicKey вместо secretKey

Шифрование требует именно приватного ключа отправителя и публичного ключа получателя:

nacl.box(message, nonce, recipientPublicKey, senderSecretKey);

Ошибочное переключение параметров приводит к невозможности расшифровки.


Повторное использование nonce

Nonce (одноразовый случайный параметр) должен быть уникальным для каждой операции:

  • повтор nonce + ключ = криптографическая уязвимость
  • допускается использование nacl.randomBytes(24)

Потеря secretKey

Без приватного ключа восстановление данных невозможно. Алгоритм не предусматривает backdoor или recovery.


Размеры и ограничения

  • publicKey: 32 байта
  • secretKey: 32 байта
  • nonce: 24 байта
  • overhead криптосистемы: фиксирован

Это делает систему компактной и подходящей для клиентских приложений, WebSocket-соединений и мессенджеров.


Производительность генерации

Генерация ключевой пары:

  • выполняется за миллисекунды
  • не требует значительных вычислений
  • не является узким местом системы

Основная нагрузка возникает при массовом шифровании сообщений, а не при генерации ключей.


Совместимость TweetNaCl.js и nacl.js

TweetNaCl.js является минималистичной JavaScript-портированной реализацией NaCl. В контексте ключевой пары:

  • API идентичен
  • структура результата одинакова
  • поведение криптографически эквивалентно

Различия могут быть только в сборках и окружении исполнения.


Практическая роль в архитектуре приложений

nacl.box.keyPair() обычно используется:

  • при регистрации пользователя
  • при создании сессии обмена сообщениями
  • при инициализации P2P соединений
  • в end-to-end шифровании мессенджеров

Каждый участник коммуникации имеет собственную пару ключей, формируя основу асимметричной криптографии.


Детали внутренней логики

На уровне реализации:

  1. генерируется случайный 32-байтовый seed
  2. seed преобразуется в scalar private key
  3. выполняется scalar multiplication на базовой точке Curve25519
  4. получается publicKey

Этот процесс гарантирует, что публичный ключ математически связан с приватным, но обратное вычисление невозможно.


Особенности безопасности Curve25519

Curve25519 используется благодаря:

  • устойчивости к известным атакам на ECC
  • высокой скорости вычислений
  • устойчивости к side-channel атакам (при корректной реализации)
  • широкому принятию в современных протоколах

Роль в экосистеме NaCl

nacl.box.keyPair() является базовым строительным блоком для:

  • nacl.box (шифрование)
  • nacl.box.open (дешифрование)
  • nacl.box.before (предварительное вычисление shared key)

Без корректной генерации ключей невозможна работа всей схемы public-key encryption в библиотеке.