本文へスキップ
ecdsa.com
コマンドライン

GnuPG (gpg)

GnuPG 2.4 は、gpg の基盤となる OpenPGP 実装です。デフォルトの Ed25519 鍵は 3 年で期限切れになり、ECDSA は NIST、Brainpool、secp256k1 の各曲線で RFC 6979 の決定的な署名を行い、Ed25519 はダイジェストに署名します。ポスト量子アルゴリズムにはまだ対応していません。
ライセンス
GPL-3.0-or-later
確認したバージョン
2.4.9 ·
ソースコード
dev.gnupg.org/source/gnupg/
ドキュメント
www.gnupg.org/documentation/manuals/gnupg/

互換性の概要

ECDSAEdDSA

ECDSA の曲線
P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1
EdDSA
Ed25519Ed448
公開鍵の形式
opensshopenpgp

GnuPG は OpenPGP を実装しており、ファイルやメールの署名と暗号化、多くの Linux ディストリビューションでのパッケージ署名、git のコミット署名に使われています。ここに挙げた事実は、Alpine 3.24.2 のパッケージ(GnuPG 2.4.9、libgcrypt 1.12.2)で確認したものです。ユーザー ID だけを指定して gpg --quick-gen-key を実行すると、署名と証明(certification)用の Ed25519 の主鍵と、暗号化用の Curve25519(cv25519)の副鍵が、どちらもバージョン 4 の鍵として作成されます。有効期限を指定しない限り、主鍵は 1095 日(3 年)後に期限切れになります。鍵と署名は OpenPGP インスペクターで解析できます。

ECDSA 鍵は P-256、P-384、P-521、secp256k1 と 3 つの Brainpool r1 曲線で作成できます。nistp224 は「Unknown elliptic curve」で失敗します。署名パケットは r と s を 2 つの OpenPGP MPI として格納しており、DER でも固定長の r‖s でもありません。P-256 の署名はデフォルトで SHA-256 を使います。P-384 の鍵で SHA-256 を指定すると「signing failed: Invalid length」で失敗しますが、P-256 の鍵での SHA-512 は受け付けられます。

nonce は決定的です。時刻を固定する(--faked-system-time)と、同じファイルへの 2 つの署名はバイト単位で一致し、r は独立した RFC 6979 実装の値と一致しました。そのため、同じデータに 2 回署名しても、危険な形で nonce が再利用されることはありません。s は正規化されず、s を n − s に置き換えた署名も有効な署名として検証を通りました。署名が一意であることを前提とする場面では、この点に注意が必要です。

Ed25519 署名は従来の EdDSA 形式(アルゴリズム 22)を使います。R と S は MPI であり、Ed25519 が署名するのはファイルそのものではなく、ファイルと署名のハッシュ対象フィールドから計算した SHA-512 ダイジェストです。R と S をそれぞれ先頭からゼロで埋めて 32 バイトにした R‖S は、OpenSSL でそのダイジェストに対しては検証を通り、ファイルに対しては失敗しました。つまり、汎用の Ed25519 ライブラリで gpg の署名を検証するには、まず OpenPGP のダイジェストを計算する必要があります。Ed448 の鍵は、GnuPG が準拠する LibrePGP ドラフトの形式であるバージョン 5 の鍵パケットとして作成されます。一方、確認したデフォルトの鍵、ECDSA 鍵、Ed25519 鍵はすべてバージョン 4 でした。Ed448 を選ぶ前に、相手側がバージョン 5 を読み込めることを確認してください。このリリースにはポスト量子アルゴリズムがありません。公開鍵アルゴリズムの一覧は RSA、ELG、DSA、ECDH、ECDSA、EDDSA で、ky768_cv25519 は未知の曲線として拒否されます。

スクリプトでは、人間向けのテキストではなく、機械可読な --status-fd の出力を読んでください。有効な署名では GOODSIG と VALIDSIG が出力されて終了ステータスは 0、別のデータに対する署名では BADSIG で終了ステータスは 1、途中で切れたパケットでは NODATA で終了ステータスは 2 でした。フィンガープリントと ! を指定した gpg --export-ssh-key は、ECDSA 鍵と Ed25519 鍵について OpenSSH の公開鍵の行を出力します。一方、--export と --export-secret-keys は OpenPGP パケットを出力します。

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

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

  • デフォルトの鍵の種類

    ed25519cv25519

    ユーザー ID だけを指定した --quick-gen-key は、ed25519 の主鍵(用途 sc)と cv25519 の副鍵(用途 e)を作成しました。

    チェックによる記録
    fact default-key-type = ed25519, cv25519
    evidence default-key-type: pub ed25519 (algorithm 22, usage scESC); sub cv25519 (algorithm 18, usage e)
  • 鍵のデフォルトの有効期限

    1095d

    主鍵の有効期限は、作成から 1095 日後でした。

    チェックによる記録
    fact default-key-expiry = 1095d
    evidence default-key-expiry: expiration minus creation of the primary key: 1095 days
  • OpenPGP 鍵のバージョン

    v4

    デフォルトの鍵は、バージョン 4 の鍵パケットとしてエクスポートされました。

    チェックによる記録
    fact openpgp-key-version = v4
  • Ed448 鍵のバージョン

    v5

    ed448 の鍵はバージョン 5 の鍵パケットとしてエクスポートされ、デフォルトの鍵、ECDSA 鍵、ed25519 の鍵はバージョン 4 でした。

    チェックによる記録
    fact ed448-key-version = v5
    evidence ed448-key-version: key packet versions: default v4, nistp256 v4, nistp384 v4, nistp521 v4, secp256k1 v4, brainpoolP256r1 v4, brainpoolP384r1 v4, brainpoolP512r1 v4, ed25519 v4, ed448 v5
  • ECDSA の曲線

    P-256P-384P-521secp256k1brainpoolP256r1brainpoolP384r1brainpoolP512r1

    7 つの曲線すべてで鍵を生成し、署名と検証ができました。nistp224 は「Unknown elliptic curve」を返しました。

    チェックによる記録
    fact ecdsa-curves = P-256, P-384, P-521, secp256k1, brainpoolP256r1, brainpoolP384r1, brainpoolP512r1
    evidence ecdsa-curves: nistp224: gpg: Key generation failed: Unknown elliptic curve
  • EdDSA

    Ed25519Ed448

    ed25519 と ed448 の鍵で署名と検証ができました。

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

    none

    gpg --version の Pubkey: 行にはポスト量子アルゴリズムがなく、ky768_cv25519 の鍵生成は失敗しました。

    チェックによる記録
    fact pqc = none
    evidence pqc: --quick-gen-key with ky768_cv25519: gpg: Key generation failed: Unknown elliptic curve
  • ECDSA 署名のエンコーディング

    openpgp-mpi

    P-256 の署名パケット(アルゴリズム 19)は、r と s の 2 つの MPI で終わります。

    チェックによる記録
    fact ecdsa-signature-encoding = openpgp-mpi
    evidence ecdsa-signature-encoding: P-256 detached signature: version=4 pubkey-algo=19 hash-algo=8 mpis=2
  • デフォルトのハッシュ

    sha256

    デフォルトの P-256 署名は、ハッシュアルゴリズム 8(SHA-256)を指定しています。

    チェックによる記録
    fact ecdsa-default-hash = sha256
  • ECDSA の nonce

    rfc6979

    時刻を固定して作成した 2 つの署名は同一で、r は RFC 6979 のリファレンス実装が生成した r と一致しました。

    チェックによる記録
    fact ecdsa-nonce = rfc6979
    evidence ecdsa-nonce: two signatures of one file with a frozen clock are identical; r equals the RFC 6979 reference signer's r: yes
  • 署名時の low-S

    either

    s は正規化されません。32 個の署名の中に high-S の値が現れました。

    チェックによる記録
    fact ecdsa-low-s-sign = either
    evidence ecdsa-low-s-sign: signatures with s > n/2 among 32: yes
  • 検証時の high-S

    accepts-high-s

    s を n − s に置き換えた署名も、GOODSIG と終了ステータス 0 で検証を通りました。

    チェックによる記録
    fact ecdsa-low-s-verify = accepts-high-s
    evidence ecdsa-low-s-verify: s replaced by n - s: exit 0, status NEWSIG KEY_CONSIDERED SIG_ID GOODSIG VALIDSIG TRUST_ULTIMATE
  • 曲線に紐付くハッシュ

    enforced

    P-384 の鍵での --digest-algo SHA256 は「Invalid length」で失敗し、P-256 の鍵での SHA-512 では署名できました。

    チェックによる記録
    fact ecdsa-hash-curve-binding = enforced
    evidence ecdsa-hash-curve-binding: --digest-algo SHA256 with a P-384 key: gpg: signing failed: Invalid length; --digest-algo SHA512 with a P-256 key: signed
  • 検証失敗時の出力

    distinguishable

    誤ったメッセージでは BADSIG と終了ステータス 1、途中で切れたパケットでは NODATA と終了ステータス 2 です。

    チェックによる記録
    fact cli-verify-output = distinguishable
    evidence cli-verify-output: valid: exit 0, status NEWSIG KEY_CONSIDERED SIG_ID GOODSIG VALIDSIG TRUST_ULTIMATE
    evidence cli-verify-output: wrong message: exit 1, status NEWSIG KEY_CONSIDERED BADSIG FAILURE
    evidence cli-verify-output: truncated signature packet: exit 2, status NODATA FAILURE
  • Ed25519 署名の対象範囲

    digest

    R‖S は、openssl pkeyutl で OpenPGP の署名ダイジェストに対しては検証を通り、ファイルに対しては失敗しました。

    チェックによる記録
    fact ed25519-signed-input = digest
    evidence ed25519-signed-input: version=4 pubkey-algo=22 hash-algo=10 mpis=2; R || S checked by openssl pkeyutl over the signature digest: Signature Verified Successfully; over the file: Signature Verification Failure
  • 公開鍵の形式

    opensshopenpgp

    gpg --export は OpenPGP パケットを出力します。--export-ssh-key は ecdsa-sha2-nistp256 と ssh-ed25519 の行を出力しました。

    チェックによる記録
    fact public-key-formats = openssh, openpgp
    evidence public-key-formats: gpg --export: public key packet; gpg --export-ssh-key: P-256 key ecdsa-sha2-nistp256, Ed25519 key ssh-ed25519
  • 秘密鍵の形式

    openpgp

    gpg --export-secret-keys は OpenPGP の秘密鍵パケットを出力します。

    チェックによる記録
    fact private-key-formats = openpgp
    evidence private-key-formats: gpg --export-secret-keys: secret key packet

使い方

デフォルトの鍵を作成し、ASCII アーマー形式の分離署名を作成して、ステータス出力で検証します。
export GNUPGHOME="$(mktemp -d)"
# With only a user ID: an Ed25519 signing key plus a Curve25519 encryption subkey, expiring in 3 years.
gpg --batch --pinentry-mode loopback --passphrase '' --quick-gen-key 'Alice <alice@example.com>'

printf 'order #1042 approved\n' > order.txt
gpg --batch --yes --armor --detach-sign order.txt   # writes order.txt.asc

# Scripts should read --status-fd: GOODSIG and VALIDSIG for a valid signature, BADSIG otherwise.
gpg --batch --status-fd 1 --verify order.txt.asc order.txt 2>/dev/null | grep -E '^\[GNUPG:\] (GOODSIG|VALIDSIG)' | cut -d' ' -f2

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

セットアップ
apk add --no-cache -q gnupg=2.4.9-r1 >/dev/null
コマンド
sh sign.sh
一致すべき出力(正規表現)
GOODSIG\nVALIDSIG

ライブラリ