Криптографическая подпись в Web Crypto API строится на асимметричной модели, в которой используется пара ключей: закрытый для создания подписи и открытый для её проверки. Основное свойство такой схемы — невозможность вычислить корректную подпись без владения приватным ключом, при этом любой обладатель публичного ключа может убедиться в подлинности данных.
Цифровая подпись представляет собой результат криптографического преобразования хеш-значения сообщения с использованием закрытого ключа. В большинстве современных схем подписи не подписывается сам текст напрямую — сначала вычисляется хеш, затем он криптографически защищается.
Фундаментальные свойства цифровой подписи:
Web Crypto API реализует эти свойства через интерфейс
SubtleCrypto.
В браузерной криптографии поддерживаются несколько алгоритмов цифровой подписи:
Наиболее распространённые в современных системах:
Перед подписанием данных необходимо создать ключевую пару. В Web
Crypto API это выполняется через
crypto.subtle.generateKey.
Пример генерации ключей ECDSA:
const keyPair = await crypto.subtle.generateKey(
{
name: "ECDSA",
namedCurve: "P-256"
},
true,
["sign", "verify"]
);
Параметры:
name — алгоритмnamedCurve — кривая (P-256, P-384, P-521)extractable — можно ли экспортировать ключkeyUsages — назначение ключейRSA-PSS пример:
const keyPair = await crypto.subtle.generateKey(
{
name: "RSA-PSS",
modulusLength: 2048,
publicExponent: new Uint8Array([1, 0, 1]),
hash: "SHA-256"
},
true,
["sign", "verify"]
);
Здесь важны параметры:
modulusLength — длина RSA ключаpublicExponent — обычно 65537hash — хеш-функция, используемая внутри схемыПодписание осуществляется методом crypto.subtle.sign.
Данные должны быть представлены в виде ArrayBuffer.
Пример ECDSA подписи:
const encoder = new TextEncoder();
const data = encoder.encode("Сообщение для подписи");
const signature = await crypto.subtle.sign(
{
name: "ECDSA",
hash: "SHA-256"
},
privateKey,
data
);
Для RSA-PSS:
const signature = await crypto.subtle.sign(
{
name: "RSA-PSS",
saltLength: 32
},
privateKey,
data
);
Результат — бинарная подпись в формате ArrayBuffer.
Проверка выполняется через crypto.subtle.verify.
const isValid = await crypto.subtle.verify(
{
name: "ECDSA",
hash: "SHA-256"
},
publicKey,
signature,
data
);
Возвращаемое значение — булево:
true — подпись корректнаfalse — данные изменены или ключ не соответствуетВо многих схемах подписи Web Crypto API хеширование выполняется автоматически внутри алгоритма. Однако концептуально процесс выглядит так:
Хеш-функция играет критическую роль:
Web Crypto API работает с несколькими форматами ключей:
Экспорт публичного ключа:
const exported = await crypto.subtle.exportKey(
"spki",
publicKey
);
Импорт:
const key = await crypto.subtle.importKey(
"spki",
exported,
{
name: "ECDSA",
namedCurve: "P-256"
},
true,
["verify"]
);
JWK формат удобен для передачи через API:
const jwk = await crypto.subtle.exportKey("jwk", publicKey);
RSA-PSS основан на факторизации больших чисел. Его особенности:
Ключевая особенность — вероятностный характер подписи: одинаковые данные дают разные подписи.
ECDSA основан на эллиптических кривых:
Недостаток — чувствительность к качеству генерации случайных чисел. Ошибки RNG могут привести к утечке приватного ключа.
Хеш-функции, используемые в Web Crypto API:
Хеширование выполняет роль “сжатия” данных до фиксированного размера перед подписью.
Внутренний принцип:
signature = Sign(privateKey, Hash(message))
На практике при работе с Web Crypto API часто возникают системные ошибки:
Подпись и проверка должны использовать одинаковые параметры:
hashnamedCurve или параметры RSAECDSA, RSA-PSS)Web Crypto API не работает со строками напрямую:
ArrayBufferTextEncoder / TextDecoderПодпись — это бинарный массив. При преобразовании в строку без кодирования Base64 происходит повреждение данных.
SaltLength должен соответствовать криптографической конфигурации и часто равен длине хеша.
Криптографическая корректность в браузере зависит не только от алгоритма, но и от архитектуры использования.
Закрытый ключ не должен:
Ключи должны:
Перед подписанием необходимо обеспечить:
Рекомендуемые варианты:
Нельзя сравнивать подписи как строки. Корректный способ —
использование crypto.subtle.verify.
Цифровая подпись в Web Crypto API является низкоуровневым механизмом, в котором безопасность определяется не только криптографическим алгоритмом, но и строгим соблюдением форматов данных, параметров ключей и корректной обработкой бинарных структур.