OpenSSL は、C 向けに、またバインディングを通じて Node.js、Python、PHP、Ruby 向けに libcrypto と libssl を提供しており、多くの人が最初に触れる openssl コマンドも含まれています。ここに挙げる事実は、Alpine 3.24.2 のディストリビューションパッケージである OpenSSL 3.5.9(3.5 の長期サポート系列)で確認したものです。確認時点のアップストリームの最新リリースは 3.6.5 と 4.0.3 であり、これらでは動作が異なる可能性があります。
EVP_DigestSign と openssl dgst -sign が生成する ECDSA 署名は ASN.1 DER で、P-256 では 70〜72 バイトです。nonce-type パラメーターを 1 に設定しない限り(OpenSSL 3.2 以降)nonce はランダムで、1 に設定すると RFC 6979 の P-256 テストベクトルを再現できました。コマンドラインでは、サンプルのとおり、このパラメーターは pkeyutl からしか指定できません。s は正規化されず、検証は high-S も受け入れます。EVP_PKEY_sign は自分で計算したダイジェストに署名します。
不正な形式の入力は、偽という結果ではなくエラーになります。EVP_DigestVerify は 64 バイトの生の r‖s 署名に対して -1 を返し、CLI もそれをエラーとして示しました。openssl dgst -verify は、正しい署名には Verified OK を、別のデータに対する有効な署名には Verification failure を、DER ではない 64 バイトのデータには Error verifying data を出力しました。そのため 3.5.9 では、形式の問題と偽造とを文言で区別できます。生の署名は DER ⇄ 生形式の変換ツールで変換するか、OpenSSL コマンドビルダーで正しいコマンドを組み立ててください。
曲線は P-224 から P-521 まで、secp256k1、Brainpool の r1 曲線に対応し、さらに Ed25519 と Ed448 も使えます。3.5 以降はデフォルトのプロバイダーが ML-KEM、ML-DSA、SLH-DSA と、TLS のハイブリッドグループ X25519MLKEM768 も実装しています。鍵の形式は、秘密鍵が PKCS#8(暗号化なし・暗号化あり)または SEC1、公開鍵が SPKI または証明書です。genpkey は -----BEGIN PRIVATE KEY----- を書き出し、従来の openssl ec は -----BEGIN EC PRIVATE KEY----- を書き出します。EC というラベルの付いたファイルは常に秘密鍵です。
検索されることの多いエラーメッセージのほとんどは鍵の読み込みで発生するもの(デコーダーが出す no start line、header too long、unsupported)で、リンク先のエラーページで解説しています。