エラーメッセージ
InvalidAccessError: Failed to execute 'exportKey' on 'SubtleCrypto': key is not extractable
別の表記
- InvalidAccessError: The CryptoKey is nonextractable
- InvalidAccessError: A parameter or an operation is not supported by the underlying object
上記は Chromium のテキストです。Safari/WebKit は「The CryptoKey is nonextractable」、Firefox は「A parameter or an operation is not supported by the underlying object」です。いずれも `InvalidAccessError` の DOMException です。
WebCrypto SubtleCrypto.exportKey / wrapKey — DOMException InvalidAccessError (Chromium / Firefox / WebKit)
このエラーの意味
この鍵は `extractable: false` で作成されているため、その鍵データを読み出すことは `exportKey` でも `wrapKey` でも決してできません。それがこのフラグの目的です。秘密鍵はブラウザーの暗号実装の内部にとどまり、スクリプト(注入されたスクリプトを含む)は鍵を使えても盗むことはできません。生成した鍵ペアの公開鍵は常にエクスポートできます。このエラーが関係するのは秘密鍵と共通鍵です。
発生する理由
鍵が意図的にエクスポート不可として生成されている
よくあるライブラリやパスキーに似た設計では、デバイスに紐付いた鍵をこの方法で作成します。後から追加した機能で秘密鍵の表示、バックアップ、同期をしようとすると、この設計上の判断と衝突します。
IndexedDB から復元した鍵はフラグを保持している
ときどきIndexedDB に保存した CryptoKey は、作成されたときとまったく同じ状態で戻ってきます。後からエクスポート可能にすることはできません。
ライブラリのデフォルト設定
ときどき一部の JOSE ライブラリや E2E 暗号化ライブラリは、デフォルトで `extractable: false` の鍵を生成します。その場合、それらのライブラリのエクスポート用ヘルパーは、秘密鍵に対しては失敗します。
修正方法
- 1.
秘密鍵はオブジェクトのまま保持し、公開鍵をエクスポートする
エクスポート不可の鍵は `CryptoKey` オブジェクトとして IndexedDB に保存し、公開鍵(SPKI または JWK)だけを共有してください。これで、このフラグによる保護が維持されます。
js const pair = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, false, ["sign", "verify"]); // Persist the non-extractable private key as a CryptoKey object (structured clone), never as bytes: await idbPut("signing-key", pair.privateKey); // Share only the public key: const jwk = await crypto.subtle.exportKey("jwk", pair.publicKey); - 2.
エクスポートが必須の場合に限り、エクスポート可能な鍵を作成する
秘密鍵をどうしてもブラウザーの外に出さなければならない場合は、エクスポート可能として生成し、別の鍵でラップした状態でのみエクスポートしてください。
js // Only if the private key must leave the browser (e.g. a user-initiated, encrypted backup): const pair = await crypto.subtle.generateKey({ name: "ECDSA", namedCurve: "P-256" }, true, ["sign", "verify"]); const wrapped = await crypto.subtle.wrapKey("pkcs8", pair.privateKey, backupKey, { name: "AES-GCM", iv });