Skip to content
ecdsa.com
WebCrypto SubtleCrypto.exportKey / wrapKeyWebCrypto

Error message

InvalidAccessError: Failed to execute 'exportKey' on 'SubtleCrypto': key is not extractable

Also printed as

  • InvalidAccessError: The CryptoKey is nonextractable
  • InvalidAccessError: A parameter or an operation is not supported by the underlying object

Chromium's text is above; Safari/WebKit: "The CryptoKey is nonextractable"; Firefox: "A parameter or an operation is not supported by the underlying object". All are `InvalidAccessError` DOMExceptions.

WebCrypto SubtleCrypto.exportKey / wrapKey — DOMException InvalidAccessError (Chromium / Firefox / WebKit)

What it means

The key was created with `extractable: false`, so its material can never be read back — not by `exportKey`, not by `wrapKey`. That is the point of the flag: the private key stays inside the browser's crypto implementation, and script (including injected script) can use it but not steal it. Public keys of a generated pair are always exportable; this error concerns private and secret keys.

Why it happens

How to fix it

  1. 1.

    Keep the private key as an object, export the public key

    Store non-extractable keys as `CryptoKey` objects in IndexedDB and share only the public key (SPKI or JWK). This keeps the protection the flag provides.

    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. 2.

    Create extractable keys only when export is a requirement

    If the private key genuinely must leave the browser, generate it extractable and export it only wrapped with another key.

    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 });

Related errors

Browse the full signature error database