Сообщение об ошибке
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». Во всех случаях это DOMException `InvalidAccessError`.
WebCrypto SubtleCrypto.exportKey / wrapKey — DOMException InvalidAccessError (Chromium / Firefox / WebKit)
Что это означает
Ключ создан с `extractable: false`, поэтому его ключевой материал никогда не удастся прочитать обратно — ни через `exportKey`, ни через `wrapKey`. В этом и смысл флага: закрытый ключ остаётся внутри криптографической реализации браузера, и скрипт (в том числе внедрённый) может его использовать, но не может украсть. Открытые ключи сгенерированной пары экспортируются всегда; эта ошибка касается закрытых и секретных ключей.
Почему это происходит
Ключ намеренно сгенерирован неизвлекаемым
частоБиблиотеки и схемы по образцу ключей доступа именно так создают ключи, привязанные к устройству. Функция, добавленная позже, которой нужно показать закрытый ключ, сохранить его резервную копию или синхронизировать, вступает в противоречие с этим решением.
Ключ, восстановленный из IndexedDB, сохраняет свой флаг
иногдаОбъекты CryptoKey, сохранённые в IndexedDB, возвращаются ровно такими, какими были созданы; сделать их экспортируемыми задним числом невозможно.
Значение по умолчанию в библиотеке
иногдаНекоторые библиотеки для JOSE и сквозного шифрования (E2E) по умолчанию генерируют ключи с `extractable: false`; тогда их вспомогательные функции экспорта не работают с закрытыми ключами.
Как исправить
- 1.
Храните закрытый ключ как объект, а экспортируйте открытый ключ
Храните неизвлекаемые ключи в IndexedDB как объекты `CryptoKey` и передавайте другим только открытый ключ (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 });