В JavaScript отсутствует отдельный тип «байтовая строка» в классическом смысле, однако работа с байтами является фундаментальной частью криптографии, сетевых протоколов, бинарных форматов и низкоуровневых операций. В экосистеме криптографических библиотек, включая Jsrsasign, байтовое представление данных становится ключевым элементом при формировании хэшей, цифровых подписей, ASN.1 структур и кодировании ключей.
Для работы с байтами используется набор встроенных механизмов, объединённых вокруг концепции ArrayBuffer и представлений (views).
ArrayBuffer представляет собой фиксированный блок памяти, содержащий «сырые» байты.
const buffer = new ArrayBuffer(16);
Сам по себе ArrayBuffer не предоставляет способов чтения или записи данных. Он является контейнером, поверх которого строятся представления.
Наиболее близким аналогом байтовой строки является Uint8Array — массив 8-битных беззнаковых целых.
const bytes = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]);
Каждый элемент массива представляет один байт (0–255). Именно этот тип используется в большинстве криптографических операций.
Ключевая особенность:
Строка JavaScript — это UTF-16 последовательность символов, тогда как байтовый массив — это сырые данные.
const str = "Hello";
const bytes = new Uint8Array([72, 101, 108, 108, 111]);
Эти представления не эквивалентны. Прямое преобразование требует кодировщиков.
Для преобразования строк в байты используется TextEncoder.
const encoder = new TextEncoder();
const bytes = encoder.encode("Привет");
Результат — Uint8Array, содержащий UTF-8 представление строки.
Особенность UTF-8:
Обратная операция выполняется через TextDecoder.
const decoder = new TextDecoder("utf-8");
const str = decoder.decode(bytes);
Важно учитывать корректность исходных данных: повреждённые байты приводят к заменяющим символам.
В криптографии часто используется hex-формат.
function toHex(bytes) {
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
function fromHex(hex) {
const bytes = new Uint8Array(hex.length / 2);
for (let i = 0; i < bytes.length; i++) {
bytes[i] = parseInt(hex.substr(i * 2, 2), 16);
}
return bytes;
}
Hex часто используется в Jsrsasign для представления:
Base64 применяется для передачи бинарных данных в текстовых протоколах.
function toBase64(bytes) {
let binary = "";
bytes.forEach(b => binary += String.fromCharCode(b));
return btoa(binary);
}
function fromBase64(base64) {
const binary = atob(base64);
const bytes = new Uint8Array(binary.length);
for (let i = 0; i < binary.length; i++) {
bytes[i] = binary.charCodeAt(i);
}
return bytes;
}
Base64 часто используется в формате PEM, который поддерживается Jsrsasign.
Jsrsasign активно работает с бинарными представлениями данных при:
const hash = KJUR.crypto.Util.sha256("message");
Хотя на вход может подаваться строка, внутри происходит:
При создании цифровой подписи важно, чтобы входные данные имели строгую бинарную форму.
const sig = new KJUR.crypto.Signature({ alg: "SHA256withRSA" });
sig.init(privateKey);
sig.updateHex("48656c6c6f"); // hex представление байтов
const signature = sig.sign();
Здесь вход может быть:
Jsrsasign активно использует ASN.1 для кодирования структур ключей и сертификатов.
ASN.1 всегда сериализуется в байты:
Пример логики:
SEQUENCE
├── INTEGER (version)
├── SEQUENCE (algorithm)
└── BIT STRING (key data)
В памяти это представлено как Uint8Array.
DER (Distinguished Encoding Rules) — бинарный формат ASN.1.
const der = new KJUR.asn1.DERInteger({ int: 123 });
const bytes = der.getByteArray();
Результат — массив байтов, готовый к хранению или передаче.
PEM — это Base64 поверх DER.
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkq...
-----END PUBLIC KEY-----
Процесс обработки:
Jsrsasign выполняет эти шаги автоматически, но логически они всегда опираются на байтовые операции.
JavaScript поддерживает побитовые операции только над 32-битными числами, но они часто используются внутри алгоритмов:
const a = 0x12 ^ 0x34;
Однако при работе с криптографией предпочтительнее использовать Uint8Array, чтобы избежать переполнений и потерь данных.
function concatBytes(a, b) {
const result = new Uint8Array(a.length + b.length);
result.set(a, 0);
result.set(b, a.length);
return result;
}
Такая операция часто применяется при формировании сообщений для подписи.
function equals(a, b) {
if (a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
return true;
}
В криптографии важно избегать оптимизаций, зависящих от раннего выхода, если требуется защита от timing-атак.
Перед использованием в Jsrsasign данные обычно проходят цепочку преобразований:
Любое несоответствие на уровне байтов приводит к различию подписи.
В JavaScript байтовые данные:
Типичные функции принимают несколько форм входных данных:
Внутри происходит приведение к единому байтовому представлению.
"Привет" !== new Uint8Array([...])
Ошибка возникает при попытке трактовать строку как байты напрямую.
Base64 → Base64 → ошибка интерпретации данных.
Строка с нечетным количеством символов приводит к некорректному парсингу.
Любая операция в Jsrsasign сводится к последовательности:
Таким образом, байтовые строки являются фундаментом всей криптографической модели JavaScript, несмотря на отсутствие явного примитивного типа в языке.