エラーメッセージ
Verification failure
OpenSSL 3.x での表記です。OpenSSL 1.1.x では「Verification Failure」と大文字で表記されます。openssl pkeyutl -verify は、類似の「Signature Verification Failure」を出力します。OpenSSL 3.x では、どちらにも「error:030000EA:digital envelope routines:EVP_DigestVerifyFinal:provider signature failure」のようなエラースタックの行が伴います(3.6 で再現)。そもそも DER ではない署名はここまで到達せず、「Error verifying data」というエラーになります。
このエラーの意味
openssl dgst -verify はデータのダイジェストを計算し、公開鍵で署名を検証しましたが、数学的に一致しませんでした。鍵、データ、署名はいずれも読み取れています。つまり、3 つのうち 1 つがほかの 2 つと対応していません。参考までに、成功時の出力は「Verified OK」です。
発生する理由
データが署名されたものと異なる
よくあるエディターが末尾に追加した改行、git による CRLF と LF の変換、すでにハッシュ化したダイジェストの再ハッシュ化(dgst は入力をハッシュ化するため、SHA-256 のダイジェストを渡すと、ハッシュのハッシュに署名することになります)などが該当します。
ダイジェストの不一致
よくある-sha256 で署名したのに -sha384 で検証している(または署名者のライブラリがデフォルトで別のハッシュを使っている)場合です。ダイジェストは署名された内容の一部なので、両者は同じものを使わなければなりません。
公開鍵が間違っている
ときどきローテーションされた鍵、別のサービスの鍵、または署名に使った秘密鍵とは別の秘密鍵から導出された公開鍵が該当します。
修正方法
- 1.
まず署名のエンコーディングの問題を除外する
dgst -verify は、ECDSA 署名を ASN.1 DER としてしか読み取りません。WebCrypto、JWS、Go や Python の API の「raw」モードが出力する生の r‖s 署名(P-256 ではちょうど 64 バイト、P-384 では 96 バイト)は、それより前の段階で「Error verifying data」というエラーになります。署名が DER なのにここで失敗する場合は、データ、ダイジェスト、鍵のいずれかが間違っています。下にある DER と生の形式の変換ツールは、2 つの形式を相互に変換し、厳格な検証者が拒否する非正規の high-S 値を指摘します。
- 2.
ハッシュ化済みのダイジェストを pkeyutl で検証する
手元にあるのがメッセージではなくダイジェストの場合、dgst は適切なツールではありません(再びハッシュ化してしまいます)。pkeyutl は、ダイジェストをそのまま使って検証します。
bash openssl dgst -sha256 -binary message.bin > msg.hash openssl pkeyutl -verify -pubin -inkey pub.pem \ -sigfile sig.der -in msg.hash -pkeyopt digest:sha256 - 3.
データのずれをなくす
双方が実際に扱っているバイト列そのもののダイジェストを比較してください。少しでも違いがあれば、変わったのは暗号処理ではなくメッセージです。
bash openssl dgst -sha256 message.bin # run on signer and verifier sides xxd message.bin | tail -2 # check for a sneaky trailing 0a