Механизм nacl.sign реализует цифровую подпись на основе
алгоритма Ed25519 и обеспечивает одновременно аутентификацию и
целостность сообщения. В отличие от симметричных схем, здесь
используется пара ключей: закрытый ключ для подписи и открытый для
проверки.
В библиотеке TweetNaCl.js подпись реализована через пространство имён
nacl.sign, которое включает как работу с «склеенными»
(signed message), так и с раздельными (detached signature)
подписями.
Генерация пары ключей выполняется через:
const nacl = require('tweetnacl');
const keyPair = nacl.sign.keyPair();
Результат содержит:
keyPair.publicKey — открытый ключ (32 байта)keyPair.secretKey — закрытый ключ (64 байта, включает
публичную часть)Особенность реализации заключается в том, что секретный ключ уже содержит публичную часть, что упрощает внутренние операции библиотеки.
Функция nacl.sign() создаёт сообщение, в которое
встроена подпись. Это называется подписанным сообщением (signed
message).
const message = new TextEncoder().encode("секретные данные");
const signedMessage = nacl.sign(message, keyPair.secretKey);
На выходе получается бинарный массив:
[sig || message]
где:
sig — 64-байтовая подписьmessage — исходное сообщениеДля проверки используется nacl.sign.open():
const opened = nacl.sign.open(signedMessage, keyPair.publicKey);
Если подпись корректна:
nullВнутренне выполняется:
В большинстве протоколов предпочтительнее использовать отделённую подпись.
const message = new TextEncoder().encode("сообщение");
const signature = nacl.sign.detached(message, keyPair.secretKey);
Результат:
signature — 64 байтаconst isValid = nacl.sign.detached.verify(
message,
signature,
keyPair.publicKey
);
Возвращает:
true — подпись корректнаfalse — подпись недействительнаnacl.sign)Преимущества:
Недостатки:
nacl.sign.detached)Преимущества:
Недостатки:
Типичный сценарий:
const payload = {
user: "alice",
amount: 100
};
const message = new TextEncoder().encode(JSON.stringify(payload));
const signature = nacl.sign.detached(message, keyPair.secretKey);
Передача:
{
"payload": "{...}",
"signature": "base64..."
}
Проверка на стороне получателя:
const valid = nacl.sign.detached.verify(
message,
signature,
publicKey
);
TweetNaCl.js работает исключительно с Uint8Array.
Поэтому любые строки требуют преобразования:
const encode = (str) => new TextEncoder().encode(str);
const decode = (bytes) => new TextDecoder().decode(bytes);
При работе с сетью часто используется Base64:
const toBase64 = (bytes) =>
btoa(String.fromCharCode(...bytes));
const fromBase64 = (str) =>
new Uint8Array(atob(str).split('').map(c => c.charCodeAt(0)));
Алгоритм опирается на:
Ключевые свойства:
Любое изменение байтов приводит к:
null
при проверке.
Открытый ключ должен соответствовать секретному, иначе подпись всегда будет невалидной.
Частая ошибка — проверка строки вместо Uint8Array, что
приводит к неверной валидации.
detachedsigned messagedetacheddetachedsigned messagenacl.sign не является изолированным механизмом:
nacl.lowlevelnacl.box при комбинированных схемахnacl.hash для
предварительного хеширования данныхТакой подход позволяет строить гибридные криптосистемы, где подпись защищает не только сообщение, но и производные структуры данных.