Pular para o conteúdo
ecdsa.com
WebCrypto SubtleCrypto.exportKey / wrapKeyWebCrypto

Mensagem de erro

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

Também aparece como

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

O texto acima é o do Chromium; Safari/WebKit: “The CryptoKey is nonextractable”; Firefox: “A parameter or an operation is not supported by the underlying object”. Em todos os casos, trata-se de uma DOMException `InvalidAccessError`.

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

O que significa

A chave foi criada com `extractable: false`, então o material dela nunca pode ser lido de volta — nem com `exportKey`, nem com `wrapKey`. Esse é justamente o objetivo da flag: a chave privada fica dentro da implementação criptográfica do navegador, e scripts (inclusive scripts injetados) podem usá-la, mas não roubá-la. As chaves públicas de um par gerado são sempre exportáveis; este erro diz respeito a chaves privadas e chaves secretas.

Por que acontece

Como corrigir

  1. 1.

    Manter a chave privada como objeto e exportar a chave pública

    Armazene as chaves não extraíveis como objetos `CryptoKey` no IndexedDB e compartilhe só a chave pública (SPKI ou JWK). Assim, a proteção que a flag oferece é preservada.

    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.

    Criar chaves extraíveis só quando a exportação for um requisito

    Se a chave privada realmente precisar sair do navegador, gere-a como extraível e exporte-a somente encapsulada com outra chave.

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

Erros relacionados

Ver toda a base de erros de assinatura