本文へスキップ
ecdsa.com
Python

python-ecdsa

tlsfuzzer による純粋な Python 実装の ECDSA・EdDSA ライブラリです。指定しなければ SHA-1 で生の r‖s 署名を作り、nonce はデフォルトでランダム(RFC 6979 はメソッドを 1 つ替えるだけで使えます)、公開鍵はプレフィックスのない x‖y です。
ライセンス
MIT
確認したバージョン
0.19.2 ·
ソースコード
github.com/tlsfuzzer/python-ecdsa
ドキュメント
ecdsa.readthedocs.io/en/latest/

互換性の概要

ECDSAEdDSA

ECDSA の曲線
P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
公開鍵の形式
spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy
秘密鍵の形式
pkcs8-pemsec1-pemsec1-derraw-scalar

ecdsa は ECDSA と EdDSA を純粋な Python で実装しており、内部で OpenSSL を使いません。そのためどこにでも簡単にインストールでき、テストベクトルの作成や教育用途に便利です。また、python-jose のような古い JOSE スタックの依存関係として今も使われています。メンテナーはサイドチャネル攻撃への耐性を考慮した設計ではないと明言しているため、本番環境での署名には pyca/cryptography か coincurve を使うべきです。

相互運用の失敗は、ほぼすべてが 3 つのデフォルト値に起因します。まず、ハッシュのデフォルトは SHA-1 です。hashfunc を指定せずに作成した SigningKey は SHA-1 のダイジェストに署名します。チェックではデフォルトの署名を pyca/cryptography で検証してこれを確認しました。SHA-1 では検証を通り、SHA-256 では失敗しました。次に、署名は DER ではなく生の r‖s(P-256 では 64 バイト)です。そして VerifyingKey.to_string() は 0x04 のプレフィックスがない 64 バイトの x‖y を返しますが、OpenSSL、Go、WebCrypto はこれを点として読み取りません。

署名には、sign_deterministic() を呼ばない限りランダムな nonce が使われます。この関数は RFC 6979 の P-256 テストベクトルを再現しました。*_canonize 系のエンコーダーを選ばない限り s は計算されたまま残り(約半数は high-S です)、検証は low-S と high-S のどちらも受け入れます。エンコーディングはすべて呼び出しごとに sigencode= と sigdecode= で選ぶため、署名側と検証側は同じ組み合わせを指定しなければなりません。DER 署名をデフォルトの生形式用デコーダーに渡すと BadSignatureError: Malformed formatting of signature が発生します。

対応する曲線は Python ライブラリの中で最も幅広く、P-224〜P-521、secp256k1、Brainpool に加えて Ed25519 と Ed448 も使えます。鍵は SPKI の PEM または DER、SEC1 の点、生のスカラーとして読み込めます。to_pem() は、format="pkcs8" を渡さない限り SEC1 の EC PRIVATE KEY を書き出します。

サンプルでは 3 つのデフォルトをまとめて修正しています。SHA-256、決定的 nonce、正規化された low-S の DER 署名です。生成した署名を別のライブラリと突き合わせるには、DER ⇄ 生形式の変換ツールとテストベクトルを使うのが最も手早い方法です。

事実とその根拠となるチェック

以下の内容はドキュメントからの転記ではありません。2026年10月20日に、チェック content/libraries/verify/python-ecdsa/run.sh がコンテナイメージ python:3.14.8-slim-trixie 内で python-ecdsa 0.19.2 を実行しました。このページの内容はその出力と一致していなければならず、各事実には該当する出力行を示しています。

  • ECDSA 署名のエンコーディング

    raw

    sign() は P-256 で 64 バイトの生の r‖s を返します。sigencode_der を使うと DER になり、その場合は検証に sigdecode_der が必要です。

    チェックによる記録
    fact ecdsa-signature-encoding = raw
    evidence ecdsa-signature-encoding: sigencode_der output parses as DER: True; verifies with sigdecode_der: verified
  • デフォルトのハッシュ

    sha1

    hashfunc を指定しないと、鍵は SHA-1 のダイジェストに署名します。デフォルトの署名は pyca/cryptography で SHA-1 では検証を通り、SHA-256 では通りませんでした。

    チェックによる記録
    fact ecdsa-default-hash = sha1
    evidence ecdsa-default-hash: checked by verifying the default signature in pyca/cryptography with SHA-1 and with SHA-256
  • 署名時の low-S

    either

    通常のエンコーダーは s を計算されたまま残します(64 個の署名の中に high-S が現れました)。sigencode_string_canonize と sigencode_der_canonize は s を正規化します。

    チェックによる記録
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: high-S among 64 plain signatures: True; among 64 with sigencode_string_canonize: False
    evidence ecdsa-low-s-sign: sigencode_strings_canonize exists: True
  • 検証時の high-S

    accepts-high-s

    s を n − s に置き換えた署名も検証を通りました。

    チェックによる記録
    fact ecdsa-low-s-verify = accepts-high-s
  • ECDSA の nonce

    rfc6979-opt-in

    sign() はランダムな nonce を使います。sign_deterministic() は RFC 6979 の付録 A.2.5 を再現しました。

    チェックによる記録
    fact ecdsa-nonce = rfc6979-opt-in
    evidence ecdsa-nonce: sign() repeats: False; sign_deterministic() reproduces RFC 6979 A.2.5: True
  • 署名呼び出しがハッシュする対象

    hashes-message-or-digest

    sign() はメッセージをハッシュします。sign_digest() は自分で計算したダイジェストに署名し、その署名はメッセージに対して検証を通りました。

    チェックによる記録
    fact ecdsa-prehash = hashes-message-or-digest
  • 不正な形式の ECDSA 署名

    throws

    長さの誤った入力では BadSignatureError: Malformed formatting of signature が発生します。エンコーディングは正しくても別のメッセージに対する署名では BadSignatureError: Signature verification failed が発生します。

    チェックによる記録
    fact ecdsa-malformed-signature = throws
    evidence ecdsa-malformed-signature: DER where raw is expected: BadSignatureError: Malformed formatting of signature
    evidence ecdsa-malformed-signature: 3 bytes: BadSignatureError: Malformed formatting of signature
    evidence ecdsa-malformed-signature: wrong message: BadSignatureError: Signature verification failed
  • ECDSA の曲線

    P-224P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    P-224、P-256、P-384、P-521、secp256k1、Brainpool の r1 曲線です。

    チェックによる記録
    fact ecdsa-curves = P-224, P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
  • EdDSA

    Ed25519Ed448

    Ed25519 と Ed448 です。

    チェックによる記録
    fact eddsa = Ed25519, Ed448
  • ポスト量子アルゴリズム

    none

    ポスト量子アルゴリズムには対応していません。

    チェックによる記録
    fact pqc = none
  • 公開鍵のデフォルト形式

    raw-xy

    VerifyingKey.to_string() は 0x04 のプレフィックスがない 64 バイトの x‖y を返します。SEC1 のエンコーディングにするには "uncompressed" または "compressed" を渡します。

    チェックによる記録
    fact default-public-key = raw-xy
  • 公開鍵の形式

    spki-pemspki-dersec1-uncompressedsec1-compressedraw-xy

    公開鍵は SPKI の PEM と DER、SEC1 の点、プレフィックスのない x‖y から読み込めました。

    チェックによる記録
    fact public-key-formats = spki-pem, spki-der, sec1-uncompressed, sec1-compressed, raw-xy
  • 秘密鍵の形式

    pkcs8-pemsec1-pemsec1-derraw-scalar

    秘密鍵は PKCS#8 PEM、SEC1 の PEM と DER、生のスカラーから読み込めました。to_pem() はデフォルトで -----BEGIN EC PRIVATE KEY----- を書き出します。

    チェックによる記録
    fact private-key-formats = pkcs8-pem, sec1-pem, sec1-der, raw-scalar
    evidence private-key-formats: to_pem() without format writes -----BEGIN EC PRIVATE KEY-----

使い方

SHA-256、RFC 6979 の nonce、正規化された low-S の DER エンコーディングで署名し、検証します。
import hashlib

from ecdsa import NIST256p, SigningKey
from ecdsa.util import sigdecode_der, sigencode_der_canonize

key = SigningKey.generate(curve=NIST256p, hashfunc=hashlib.sha256)  # the default would be SHA-1
public = key.get_verifying_key()
message = b"order #1042 approved"

# RFC 6979 nonce, DER encoding, s normalized to the low half: what OpenSSL-style
# verifiers read and what strict low-S verifiers accept.
signature = key.sign_deterministic(message, sigencode=sigencode_der_canonize)
again = key.sign_deterministic(message, sigencode=sigencode_der_canonize)

print("deterministic:", signature == again)
print("verified:", public.verify(signature, message, sigdecode=sigdecode_der))
print(public.to_pem().decode().splitlines()[0])

スニペットのテストハーネスは、すべてのサンプルをコンテナイメージ python:3.14.8-slim-trixie 内でそのまま実行し、出力が一致しなければ実行は失敗します。

セットアップ
pip install --quiet --root-user-action=ignore ecdsa==0.19.2
コマンド
python usage.py
一致すべき出力(正規表現)
deterministic: True\nverified: True\n-----BEGIN PUBLIC KEY-----

ライブラリ