В SJCL (Stanford Javascript Crypto Library) подписи строятся на основе асимметричной криптографии и используются для проверки целостности и подлинности данных. Основная идея — одна сторона создаёт подпись с использованием приватного ключа, а другая проверяет её с помощью публичного ключа.
SJCL не навязывает единый формат хранения подписи как «строку с метаданными», вместо этого работа строится вокруг структурированных объектов и сериализации данных в JSON или бинарные представления.
В контексте SJCL подпись обычно рассматривается как результат работы алгоритма цифровой подписи, например DSA или ECDSA.
Типичная структура включает:
В ECDSA-подобной схеме подпись формируется как:
Именно пара (r, s) является базовым «форматом подписи» в SJCL.
В SJCL подпись обычно хранится как объект:
{
r: "bigint-or-bitArray",
s: "bigint-or-bitArray"
}
На практике SJCL часто использует bitArray — внутренний
формат хранения битовых последовательностей.
Пример:
var signature = {
r: sjcl.bn.fromBits(rBits),
s: sjcl.bn.fromBits(sBits)
};
Где:
sjcl.bn — big number реализация библиотекиfromBits — преобразование из bitArray в числоКлючевая особенность SJCL — использование bitArray для
всех бинарных данных.
Подпись в сыром виде может быть представлена как:
[r || s]
То есть последовательное объединение двух чисел в бинарной форме.
Пример структуры bitArray:
var sigBits = rBits.concat(sBits);
где:
concat объединяет два массива битЧтобы передавать подпись по сети, SJCL предоставляет механизмы преобразования:
var hexSignature = sjcl.codec.hex.fromBits(sigBits);
var base64Signature = sjcl.codec.base64.fromBits(sigBits);
var jsonSignature = JSON.stringify({
sig: sjcl.codec.base64.fromBits(sigBits)
});
В большинстве веб-приложений именно base64 используется как стандартный формат передачи.
Если рассматривать ECDSA, то подпись формируется следующим образом:
var hash = sjcl.hash.sha256.hash(message);
Генерируется случайное число k
Вычисляется точка на кривой:
r = x1 mod n
s = k⁻¹ (hash + r * privKey) mod n
Именно (r, s) и есть итоговый формат подписи.
SJCL хранит кривые и параметры в объектной форме:
sjcl.ecc.curves.c256
Подпись связывается с конкретной кривой, поэтому её корректная интерпретация невозможна без контекста кривой.
function encodeSignature(sig) {
return sjcl.codec.base64.fromBits(
sig.r.toBits().concat(sig.s.toBits())
);
}
function decodeSignature(str) {
var bits = sjcl.codec.base64.toBits(str);
var rBits = sjcl.bitArray.bitSlice(bits, 0, 256);
var sBits = sjcl.bitArray.bitSlice(bits, 256);
return {
r: sjcl.bn.fromBits(rBits),
s: sjcl.bn.fromBits(sBits)
};
}
Здесь важно учитывать длину кривой (например, 256 бит), которая определяет границу разбиения.
Проверка подписи в SJCL использует публичный ключ:
pub.verify(hash, signature);
где signature — объект с полями r и
s.
Внутри библиотеки происходит:
Если подпись была изменена хотя бы на 1 бит, проверка провалится.
SJCL не использует ASN.1 DER формат, как OpenSSL. Подпись — это «чистые числа».
Одна и та же подпись без знания параметров кривой не интерпретируется.
Если перепутать порядок r и s или использовать неправильную длину битов, подпись становится недействительной.
Формат подписи не содержит:
Это всё должно храниться отдельно.
В реальных системах поверх SJCL обычно вводится собственный контейнер:
{
"alg": "ECDSA",
"curve": "c256",
"sig": "base64(r||s)",
"hash": "SHA-256"
}
SJCL отвечает только за математическую часть, а структура — на уровне приложения.
Если r и s имеют разную длину, объединение приводит к смещению границ.
hex и base64 нельзя смешивать при проверке.
Хотя это не проблема формата напрямую, это разрушает подпись математически, делая её восстановимой.
Фактически формат можно свести к следующему:
SJCL предоставляет минималистичный, низкоуровневый формат подписи, оставляя разработчику контроль над упаковкой, хранением и транспортировкой данных.