Skip to content

crypto: support OpenSSL STORE private keys#63949

Open
panva wants to merge 3 commits into
nodejs:mainfrom
panva:keyobject-stores
Open

crypto: support OpenSSL STORE private keys#63949
panva wants to merge 3 commits into
nodejs:mainfrom
panva:keyobject-stores

Conversation

@panva

@panva panva commented Jun 16, 2026

Copy link
Copy Markdown
Member

Allow crypto.createPrivateKey() and private-key consumers such as crypto.sign() to load private keys from WHATWG URL objects via OpenSSL STORE.

This supports both direct URL input:

import { createPrivateKey } from 'node:crypto';

const key = createPrivateKey(new URL('file:///path/to/private-key.pem'));

and object input with optional STORE credentials and OpenSSL property query metadata:

import { createPrivateKey, sign } from 'node:crypto';

const key = createPrivateKey({
  key: new URL('pkcs11:token=node;object=signing-key;type=private'),
  passphrase: process.env.PKCS11_PIN,
  properties: 'provider=pkcs11',
});

const signature = sign('sha256', Buffer.from('payload'), key);

The properties option is passed to OpenSSL as the property query used when fetching the STORE loader. It is not appended to the URL and is distinct from provider-specific URI parameters such as PKCS#11 token or object attributes.

Why URL?

Using a WHATWG URL object makes STORE-backed private keys distinguishable from all existing key input shapes. Strings, Buffers, typed arrays, and objects with format continue to represent key material in PEM, DER, JWK, or raw forms. A URL represents a reference to a key in an OpenSSL STORE.

That means this does not need to add a new recognized format string such as format: 'store'. Avoiding a new format value keeps STORE loading out of the import/export format namespace and away from generateKeyPair() / generateKeyPairSync() option handling. STORE URLs are input-only references to externally managed private keys; the JavaScript value type is the discriminator.

Permission model

STORE loading is gated by a new crypto.store permission capability. When the Permission Model is enabled, attempts to load private keys from STORE URLs require --allow-crypto-store; otherwise ERR_ACCESS_DENIED is thrown.

$ node --permission --allow-crypto-store app.mjs

The permission is exposed through the runtime permission APIs:

process.permission.has('crypto.store'); // true
process.permission.drop('crypto.store');
process.permission.has('crypto.store'); // false

For file: STORE URLs, the existing file-system permission still applies as well: Node checks fs.read for the resolved file path before calling OpenSSL.

Passphrases and errors

The passphrase option is forwarded to OpenSSL's STORE UI callback and serves as the passphrase or PIN for encrypted or token-protected private keys.

If OpenSSL requests a passphrase and none was provided, Node throws ERR_MISSING_PASSPHRASE, matching the existing encrypted private-key import behavior. Incorrect passphrases continue to surface as OpenSSL errors.

@panva panva added the wip Issues and PRs that are still a work in progress. label Jun 16, 2026
@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/actions
  • @nodejs/config
  • @nodejs/crypto
  • @nodejs/gyp
  • @nodejs/security-wg

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Jun 16, 2026
@panva panva requested a review from tniessen June 17, 2026 09:04
@panva panva force-pushed the keyobject-stores branch 2 times, most recently from 43f0d74 to 96c5081 Compare June 18, 2026 08:52
@panva panva removed the wip Issues and PRs that are still a work in progress. label Jun 18, 2026
@panva panva force-pushed the keyobject-stores branch 2 times, most recently from ddd7eaf to 35bb771 Compare June 18, 2026 09:03
jasnell
jasnell previously approved these changes Jun 19, 2026
@panva panva force-pushed the keyobject-stores branch from 35bb771 to 9d23b6b Compare June 29, 2026 13:01
@panva panva dismissed jasnell’s stale review June 29, 2026 13:02

I changed the feature shape quite a bit. Dismissing a stale review.

@panva panva force-pushed the keyobject-stores branch from f32c6b7 to dffa62d Compare June 29, 2026 16:13
Signed-off-by: Filip Skokan <panva.ip@gmail.com>
@panva panva force-pushed the keyobject-stores branch from 6f3d2cb to e496577 Compare July 8, 2026 22:27
@jasnell jasnell added the semver-minor PRs that contain new features and should be released in the next minor version. label Jul 8, 2026
@panva

This comment was marked as resolved.

@panva panva added the wip Issues and PRs that are still a work in progress. label Jul 8, 2026
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 62.13018% with 128 lines in your changes missing coverage. Please review.
✅ Project coverage is 90.20%. Comparing base (db7cc5b) to head (b1d5d84).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/crypto/crypto_keys.cc 58.97% 19 Missing and 13 partials ⚠️
src/crypto/crypto_sig.cc 62.66% 11 Missing and 17 partials ⚠️
src/crypto/crypto_rsa.cc 42.50% 15 Missing and 8 partials ⚠️
src/crypto/crypto_dsa.cc 25.92% 17 Missing and 3 partials ⚠️
src/crypto/crypto_ec.cc 6.66% 11 Missing and 3 partials ⚠️
lib/internal/crypto/keys.js 87.50% 10 Missing ⚠️
src/permission/crypto_store_permission.cc 87.50% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #63949      +/-   ##
==========================================
- Coverage   90.25%   90.20%   -0.06%     
==========================================
  Files         741      743       +2     
  Lines      241192   241478     +286     
  Branches    45421    45487      +66     
==========================================
+ Hits       217691   217819     +128     
- Misses      15085    15199     +114     
- Partials     8416     8460      +44     
Files with missing lines Coverage Δ
lib/internal/process/permission.js 81.70% <100.00%> (+0.22%) ⬆️
lib/internal/process/pre_execution.js 97.94% <100.00%> (+<0.01%) ⬆️
src/crypto/crypto_cipher.cc 76.92% <100.00%> (+0.04%) ⬆️
src/crypto/crypto_keys.h 65.11% <ø> (ø)
src/env.cc 85.50% <100.00%> (+0.04%) ⬆️
src/node_options.cc 76.65% <100.00%> (-0.08%) ⬇️
src/node_options.h 97.53% <100.00%> (+0.01%) ⬆️
src/permission/crypto_store_permission.h 100.00% <100.00%> (ø)
src/permission/permission.cc 82.56% <100.00%> (+0.24%) ⬆️
src/permission/permission.h 100.00% <ø> (ø)
... and 7 more

... and 39 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@panva panva added review wanted PRs that need reviews. and removed wip Issues and PRs that are still a work in progress. labels Jul 9, 2026
@nodejs-github-bot

This comment was marked as outdated.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

experimental Issues and PRs related to experimental features. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. review wanted PRs that need reviews. semver-minor PRs that contain new features and should be released in the next minor version.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants