{"record":{"id":"000de8ede495d963","repo":"denoland/deno","slug":"err-crypto-custom-engine-not-supported","errorCode":"ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED","errorMessage":"Custom engines not supported by this OpenSSL","messagePattern":"Custom engines not supported by this OpenSSL","errorType":"error_code","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"ext/node/polyfills/_tls_common.ts","lineNumber":544,"sourceCode":"      err.function = \"dlfcn_load\";\n      err.reason = \"could not load the shared library\";\n      err.code = \"ERR_OSSL_DSO_COULD_NOT_LOAD_THE_SHARED_LIBRARY\";\n      throw err;\n    }\n    if (options.privateKeyEngine != null) {\n      validateString(options.privateKeyEngine, \"options.privateKeyEngine\");\n    }\n    if (options.privateKeyIdentifier != null) {\n      validateString(\n        options.privateKeyIdentifier,\n        \"options.privateKeyIdentifier\",\n      );\n    }\n    if (\n      options.privateKeyEngine != null &&\n      options.privateKeyIdentifier != null\n    ) {\n      throw new ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED();\n    }\n    if (options.ecdhCurve != null) {\n      validateString(options.ecdhCurve, \"options.ecdhCurve\");\n    }\n    // Validate cert before key - Node.js processes cert first (SetCert before SetKey)\n    validateKeyCertOption(options.cert, \"options.cert\", false);\n    validateKeyCertOption(options.key, \"options.key\", true);\n    validateKeyCertOption(options.ca, \"options.ca\", false);\n\n    // Load PFX / PKCS#12 data: extract the cert + private key so they can\n    // be used by the underlying TLS implementation. Any additional certs\n    // present in the PFX are merged into `ca`. Caller-supplied `cert`/`key`\n    // (and `ca`) take precedence, matching Node, which loads PFX first and\n    // then layers explicit cert/key on top.\n    //\n    // Node accepts both a single <string>|<Buffer> and an\n    // <Array<string|Buffer|{ buf, passphrase? }>>; an empty array (which\n    // playwright passes when no PFX is configured) must be a no-op rather","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/denoland/deno/blob/89f33cbef296a2b287f323d42de54c871fa69c77/ext/node/polyfills/_tls_common.ts#L526-L562","documentation":"Deno's node:tls SecureContext polyfill cannot load private keys through an external OpenSSL engine, because Deno uses rustls instead of OpenSSL. Node allows pairing options.privateKeyEngine (an OpenSSL engine identifier) with options.privateKeyIdentifier (a key id like 'pkcs11:...') to reference a key held in an HSM or smartcard. This polyfill (ext/node/polyfills/_tls_common.ts:544) rejects that combination up front with ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED.","triggerScenarios":"Calling tls.createSecureContext({ privateKeyEngine: 'pkcs11', privateKeyIdentifier: 'pkcs11:...' }), or constructing a tls.Server / https server whose options object carries both privateKeyEngine and privateKeyIdentifier (both must be non-null for the throw to fire).","commonSituations":"Running Node code that stores TLS keys on an HSM, YubiKey, or PKCS#11 token; enterprise apps using engine-based key loading; also triggered indirectly by packages like node-keytar or pkcs11js wrapping TLS setup. Migrating such services to Deno without changing key sourcing.","solutions":["Export the key from the engine/HSM into a PEM file and pass it via options.key (with options.passphrase if encrypted) instead of privateKeyEngine/privateKeyIdentifier","If the key cannot leave the HSM, run that service under Node.js rather than Deno, since engine-backed keys are architecturally unsupported under rustls","Gate engine-based TLS setup behind a runtime check (e.g. process.versions.nsx or a feature flag) so Deno deployments take a non-engine code path"],"exampleFix":"// before\nconst ctx = tls.createSecureContext({\n  privateKeyEngine: 'pkcs11',\n  privateKeyIdentifier: 'pkcs11:token=mytok;object=mykey',\n});\n\n// after\nconst ctx = tls.createSecureContext({\n  key: fs.readFileSync('/etc/tls/server.key.pem'),\n  passphrase: process.env.KEY_PASSPHRASE,\n});","handlingStrategy":"type-guard","validationCode":"function usesCustomEngine(options) {\n  return options?.privateKeyEngine != null && options?.privateKeyIdentifier != null;\n}\nif (usesCustomEngine(tlsOptions)) {\n  throw new Error('engine-backed keys unsupported in this runtime; provide options.key');\n}","typeGuard":"function hasEngineKey(o) {\n  return typeof o === 'object' && o !== null &&\n    o.privateKeyEngine != null && o.privateKeyIdentifier != null;\n}","tryCatchPattern":"try { tls.createSecureContext(opts); } catch (e) { if (e.code === 'ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED') { /* fall back to PEM key path */ } else throw e; }","preventionTips":["Keep PEM key paths in runtime config; treat engine identifiers as Node-only deployment detail","Assert at startup that tls key options contain either key or a PEM path, never engine+identifier pairs"],"tags":["tls","crypto","hsm","pkcs11","deno-compat"],"backgroundTag":null,"analyzedSha":"89f33cbef296a2b287f323d42de54c871fa69c77","analyzedAt":"2026-08-16T07:54:21.310Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}