Выбор между P-256 и secp256k1 редко бывает свободным: кривая обычно приходит вместе с экосистемой. Обе — 256-битные кривые Вейерштрасса простого порядка со стойкостью около 128 бит и одинаковыми размерами ключей и подписей, но они различаются происхождением, набором платформ, которые их поддерживают, и соглашениями, надстроенными поверх них, а ошибки возникают именно там, где данные переходят из одного мира в другой.
Коротко о главном
| P-256 (secp256r1, prime256v1) | secp256k1 | |
|---|---|---|
| Где определена | NIST SP 800-186, § 3.2.1.3; SEC 2, § 2.4.2 | SEC 2, § 2.4.1; SP 800-186 (приложение H.2) допускает её для блокчейнов |
| Уравнение | y² = x³ − 3x + b по модулю p = 2²⁵⁶ − 2²²⁴ + 2¹⁹² + 2⁹⁶ − 1 | y² = x³ + 7 по модулю p = 2²⁵⁶ − 2³² − 977 |
| Происхождение параметров | b получен через SHA-1 из seed, выбор которого так и не объяснили | Фиксированные минимальные коэффициенты (как у кривых Коблица) |
| Кофактор, стойкость | 1, около 128 бит | 1, около 128 бит |
| Ключи и подписи | 32-байтный закрытый ключ, 33- или 65-байтный открытый ключ, 64-байтная подпись в формате raw | Те же |
| OID | 1.2.840.10045.3.1.7 | 1.3.132.0.10 |
| JOSE / COSE | ES256 / −7 | ES256K / −47 (RFC 8812) |
| Node.js 26 (OpenSSL 3.6.5), подписей / проверок в секунду | около 68 800 / 27 200 | около 6 500 / 7 200 |
| @noble/curves 2.3.0, подписей / проверок в секунду | около 3 800 / 870 | около 3 800 / 1 180 |
| WebCrypto, TLS 1.3, WebAuthn | Да | Нет |
| Secure Enclave от Apple, карты PIV, большинство HSM | Да | Редко (AWS KMS предлагает ECC_SECG_P256K1) |
| Где обычно используется | TLS-сертификаты, ключи доступа, JWT, удостоверения устройств | Bitcoin, Ethereum, Nostr, большинство EVM-сетей |
Скорость измерялась на одном ядре Apple M4 Max.
Происхождение и доверие
Кривую P-256 NIST опубликовал в 1999 году и стандартизировал в FIPS 186-2 (2000), а SECG — под именем secp256r1. Простой модуль её поля имеет особый вид, который позволяет быстро выполнять приведение по модулю, а коэффициент b получен из seed с помощью SHA-1, как предписывала принятая тогда процедура генерации «проверяемо случайных» параметров. Откуда взялся этот seed, так и не объяснили, и после разоблачений, связанных с Dual_EC_DRBG, некоторые разработчики криптосистем перестали доверять кривым NIST. Ни одной атаки на P-256, связанной с этим seed, так и не нашли, и она остаётся самой тщательно изученной из кривых, которые применяются на практике.
secp256k1 взята из SEC 2 (2000). При a = 0 и b = 7 скрывать в параметрах нечего, а особая структура кривой даёт эффективно вычислимый эндоморфизм (метод GLV), который ускоряет умножение точки на скаляр. В 2009 году выбор Сатоси Накамото для Bitcoin пал именно на неё, что по тем временам было необычно, а затем этому примеру последовал Ethereum. NIST SP 800-186 (2023) прямо разрешает secp256k1 «для приложений, связанных с блокчейнами».
Порядок обеих кривых — простое число, поэтому тонкостей с кофактором нет, и обе одинаково уязвимы для алгоритма Шора.
Поддержка в платформах и библиотеках
Именно здесь кроется настоящее различие. P-256 встроена во всё, что управляет ключами за вас: в WebCrypto любого браузера, в TLS-стеки всех операционных систем, в Secure Enclave и CryptoKit от Apple, в Android Keystore, TPM 2.0, смарт-карты PIV и во все облачные KMS. Ключи доступа используют её по умолчанию, а Baseline Requirements CA/Browser Forum допускают её для публично доверенных сертификатов.
secp256k1 нет ни в WebCrypto, ни в TLS 1.3 (ещё RFC 8422 объявил устаревшим её идентификатор для TLS 1.2), а универсальные библиотеки поддерживают её по остаточному принципу. Поэтому программы для блокчейнов опираются на специализированные реализации: в Bitcoin Core это libsecp256k1 с постоянным временем выполнения, в других проектах — @noble/curves, k256 для Rust и go-ethereum/crypto для Go. Аппаратные кошельки её поддерживают, корпоративные HSM — по-разному, а AWS KMS предлагает её как спецификацию ключа ECC_SECG_P256K1, причём только для подписи.
Производительность
Раз размеры одинаковы, скорость зависит от качества реализации, а не от кривой. В OpenSSL для P-256 есть ассемблерный код, оптимизированный вручную, а для secp256k1 — универсальный, поэтому Node.js 26.10 (OpenSSL 3.6.5) подписывает на P-256 примерно в 10 раз быстрее: около 68 800 подписей в секунду против 6 500 на одном ядре Apple M4 Max. На чистом JavaScript картина отчасти обратная: @noble/curves 2.3.0 подписывает на обеих кривых с одинаковой скоростью, а подписи secp256k1 проверяет примерно на треть быстрее благодаря эндоморфизму. libsecp256k1 ещё быстрее. Вывод: измеряйте производительность той библиотеки, которую будете поставлять, а не кривой.
Чем различаются соглашения
Одна и та же математика ECDSA в каждом из этих миров сопровождается своими правилами.
- Хеширование. P-256 используют вместе с SHA-256 (
ES256,ecdsa-with-SHA256). Bitcoin подписывает двойной хеш SHA-256, Ethereum — хеш Keccak-256, а это не то же самое, что SHA3-256. - Кодировка. X.509 и TLS используют DER, JOSE и WebCrypto — raw
r‖s; Ethereum дописывает байт восстановленияv, так что всего получается 65 байт, а в подписанных сообщениях Bitcoin перед подписью стоит байт заголовка. - Восстановление открытого ключа. Ethereum никогда не передаёт открытые ключи: восстановление (
ecrecover) вычисляет ключ по подписи, после чего сравнивается адрес. Протоколы на P-256 вместо этого передают ключ или заверяют его сертификатом. - Low-S. У каждой подписи ECDSA
(r, s)есть двойник(r, n − s). Политика ретрансляции Bitcoin и правила транзакций Ethereum (EIP-2) принимают только форму low-S; обычные реализации проверки для P-256 принимают обе. - Schnorr. Taproot добавил в Bitcoin 64-байтные подписи Schnorr по BIP-340 с 32-байтными ключами x-only на secp256k1; их же использует Nostr. Стандартной схемы Schnorr на P-256 нет.
Пластичность легко продемонстрировать в Node.js, где поддержка secp256k1 построена на OpenSSL:
import { generateKeyPairSync, sign, verify } from "node:crypto";
// Group order of secp256k1 (SEC 2 §2.4.1).
const n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141n;
const { privateKey, publicKey } = generateKeyPairSync("ec", { namedCurve: "secp256k1" });
const message = Buffer.from("hello");
const raw = sign("sha256", message, { key: privateKey, dsaEncoding: "ieee-p1363" });
const r = raw.subarray(0, 32);
const s = BigInt("0x" + raw.subarray(32).toString("hex"));
const flipped = Buffer.concat([r, Buffer.from((n - s).toString(16).padStart(64, "0"), "hex")]);
const check = (sig) => verify("sha256", message, { key: publicKey, dsaEncoding: "ieee-p1363" }, sig);
console.log("(r, s) and (r, n - s) both verify:", check(raw) && check(flipped));
console.log("exactly one of them is low-S:", (s <= n / 2n) !== (n - s <= n / 2n));
(r, s) and (r, n - s) both verify: true
exactly one of them is low-S: true
OpenSSL не приводит подписи к low-S и не требует low-S при проверке, поэтому подписи, полученные таким способом, нужно нормализовать, прежде чем их примут Bitcoin или Ethereum. Инструмент проверки подписей Ethereum отмечает значения high-S.
Что когда выбирать
- Веб, TLS, ключи доступа, JWT, ключи на мобильных и других устройствах: P-256, потому что аппаратно платформы ничего другого не предлагают.
- Bitcoin, Ethereum и EVM-сети, Nostr: secp256k1 с правилами хеширования, кодирования, восстановления ключа и low-S, принятыми в конкретной сети.
- Связка двух миров (например, кошелёк на смарт-контракте, которым управляют с помощью ключа доступа): пусть каждая кривая остаётся в своей области. Обновление Fusaka в Ethereum (декабрь 2025 года) добавило EIP-7951 — прекомпилированный контракт для проверки подписей P-256 по адресу
0x100стоимостью 6 900 единиц газа, а многие роллапы предлагают совместимый прекомпилированный контракт RIP-7212, поэтому контракты могут проверять подписи ключей доступа напрямую. - Новый протокол со свободным выбором: ни та ни другая; для программных ключей рассмотрите Ed25519, для ключей с аппаратной защитой — P-256.
Заметки о миграции
Ключи одной кривой нельзя преобразовать в ключи другой; смена кривой означает новые ключи, а в блокчейнах — и новые адреса.
- Не используйте один и тот же ключевой материал на разных кривых. 32-байтный секрет — допустимый закрытый ключ на обеих кривых, но полученные из него открытые ключи никак не связаны между собой; относитесь к этому как к ловушке, а не как к полезной возможности.
- Перенося подписание secp256k1 в HSM или KMS, убедитесь, что такой тип ключа там есть, рассчитывайте получать подписи в DER, преобразуйте их в raw
r‖s, нормализуйте к low-S и вычислите идентификатор восстановления самостоятельно, перебрав оба варианта чётности. - Проверяя подписи P-256 в контракте EVM, используйте проверенную библиотеку или прекомпилированный контракт, если сеть его предоставляет; никогда не пишите модульную арифметику на Solidity вручную.
- В JOSE переход с
ES256KнаES256меняетalg,crvи сам ключ; на переходный период опубликуйте оба ключа с разными значениямиkid.
Источники
- NIST SP 800-186, параметры эллиптических кривых (§ 3.2.1.3 и приложение H.2)
- SEC 2 v2, рекомендуемые параметры эллиптических кривых (§ 2.4.1, § 2.4.2)
- FIPS 186-5, стандарт цифровой подписи
- RFC 8812, ES256K и secp256k1 в JOSE и COSE
- BIP-340, подписи Schnorr для secp256k1
- BIP-146, LOW_S и EIP-2
- Справочник по спецификациям ключей AWS KMS