エラーメッセージ
Invalid key object type public, expected private.
2 つの名詞は、鍵と操作に応じて入れ替わります。共通鍵で sign() を呼ぶと「Invalid key object type secret, expected private.」、共通鍵で verify() を呼ぶと「Invalid key object type secret, expected private or public.」になります。verify() 自体は秘密鍵も受け付け、それに対応する公開鍵で検証します。メッセージの構造は変わりません。
Node.js crypto — TypeError, code ERR_CRYPTO_INVALID_KEY_OBJECT_TYPE
このエラーの意味
操作に合わない種類の KeyObject を渡しています。crypto.sign() には秘密鍵が必要なのに公開鍵を受け取った(あるいは verify() が共通鍵を受け取った)といったケースです。Node の鍵オブジェクトは自身の種類(公開鍵、秘密鍵、共通鍵のいずれか)を把握しており、各 API は特定の種類しか受け付けません。
発生する理由
呼び出し側で変数が入れ替わっている
よくある分割代入で publicKey と privateKey が入れ替わっている場合や、key という名前の関数の引数に、呼び出し側が持っていたほうの鍵がそのまま渡されている場合です。公開鍵で署名することは原理的に不可能であり、Node は後になって原因のわかりにくい失敗をするのではなく、その場でそれを指摘します。
読み込んだ結果が想定と異なる種類になっている
よくあるcreatePublicKey(privatePem) は意図的に公開鍵を導出します。その結果を署名用の秘密鍵として扱うコードは、ここで失敗します。同様に、実際には公開鍵が入っている PEM ファイルは、ファイル名が何であれ、種類が "public" の鍵として読み込まれます。
非対称暗号の API に共通鍵(対称鍵)を渡している
ときどきcreateSecretKey() の出力(HMAC/AES の鍵)を、ECDSA の sign/verify に渡しているケースです。共通鍵の鍵データは公開鍵署名には使えません。
修正方法
- 1.
境界で鍵の種類をアサートする
KeyObject.type は "public" | "private" | "secret" のいずれかです。1 行のアサーションを入れるだけで、わかりにくい暗号エラーが、読み込み時のわかりやすいエラーに変わります。
js import { createPrivateKey } from "node:crypto"; const signingKey = createPrivateKey(pem); if (signingKey.type !== "private") { throw new Error(`expected private key, got ${signingKey.type}`); } - 2.
鍵ペアのそれぞれを本来の操作に使う
秘密鍵で署名し、公開鍵で検証します。秘密鍵しか持っていない場合は、秘密鍵のオブジェクトをあらゆる箇所で使い回すのではなく、検証用に公開鍵を導出してください。
js import { createPublicKey, sign, verify } from "node:crypto"; const signature = sign("sha256", data, privateKey); // private → sign const publicKey = createPublicKey(privateKey); // derive public half const ok = verify("sha256", data, publicKey, signature); // public → verify