В схеме ECDSA каждая подпись зависит от случайного (или детерминированного) числа k, называемого nonce. Это значение должно быть уникальным для каждой подписи и никогда не повторяться при одном и том же приватном ключе.
Формально подпись ECDSA строится на основе:
Подпись состоит из пары значений (r, s):
где:
Критическое свойство схемы: значение k должно быть полностью уникальным и непредсказуемым.
Повторное использование nonce при одинаковом или даже разном приватном ключе приводит к полной компрометации ключа.
Если два разных сообщения подписаны с одним и тем же k, получаются подписи:
Так как k и r одинаковы, можно вычесть уравнения:
s₁ - s₂ = k⁻¹ (z₁ - z₂)
Отсюда вычисляется nonce:
k = (z₁ - z₂) / (s₁ - s₂) mod n
После восстановления k приватный ключ вычисляется напрямую:
d = (s₁ · k - z₁) / r mod n
или аналогично через вторую подпись.
Ключевой факт: один повтор nonce полностью раскрывает приватный ключ без перебора.
Наиболее частая причина — слабый или предсказуемый RNG.
Типичные проблемы:
Math.random()В JavaScript это особенно критично в средах без надежного
window.crypto.
Повтор nonce часто возникает из-за:
RFC6979 предлагает детерминированную генерацию k на основе HMAC:
Ошибки возникают, если:
Библиотека Jsrsasign реализует ECDSA и поддерживает несколько режимов генерации nonce.
Типичный пример подписи:
const sig = new KJUR.crypto.Signature({ "alg": "SHA256withECDSA" });
sig.init(privateKeyPEM);
sig.updateString("message");
const hexSig = sig.sign();
В нормальном режиме библиотека:
Однако риск появляется при неправильной конфигурации или модификации внутреннего генератора.
const sig = new KJUR.crypto.Signature({ alg: "SHA256withECDSA" });
sig.init(privateKey);
// ошибка: повторное использование без полной переинициализации
sig.updateString("msg1");
const s1 = sig.sign();
sig.updateString("msg2");
const s2 = sig.sign();
Если внутреннее состояние nonce не сбрасывается корректно, возможен reuse k.
Некоторые реализации пытаются оптимизировать подпись:
sig.setParameters({ k: customK });
Любая ручная подстановка nonce:
В многопоточных или асинхронных системах:
При наличии двух подписей:
и совпадающего r (признак одинакового nonce), атакующий вычисляет:
Это не требует знания кривой или brute-force.
В реальных системах можно выявлять проблему:
Если:
это почти всегда означает повтор nonce.
Если известны подписи, можно проверить:
на корректность распределения. Аномалии указывают на утечку.
Важно контролировать:
Правильное поведение библиотеки:
window.crypto.getRandomValuescrypto.randomBytesВажно, что deterministic nonce:
Для безопасности ECDSA необходимо:
Любое нарушение этих условий приводит к прямому восстановлению приватного ключа без вычислительных сложностей.
JavaScript среда усиливает риск:
Даже частичное совпадение k (например, утечка старших бит) может:
ECDSA крайне чувствителен к утечкам entropy.
При неправильной работе с nonce:
Ошибки на уровне одного компонента распространяются на всю криптосистему.
Поведение ECDSA полностью зависит от корректности генерации одного параметра. В контексте Jsrsasign любая модификация этого механизма превращает криптографическую подпись в линейное уравнение, решаемое за несколько шагов.