@@ -112,8 +112,8 @@ The following table lists the asymmetric key types recognized by the
112112
113113Asymmetric keys can be represented in several formats. ** The recommended
114114approach is to import key material into a [ ` KeyObject ` ] [ ] once and reuse it**
115- for all subsequent operations. A [ ` KeyObject ` ] [ ] avoids repeated parsing
116- and delivers the best performance.
115+ for all subsequent operations, as this avoids repeated parsing and delivers
116+ the best performance.
117117
118118When a [ ` KeyObject ` ] [ ] is not practical - for example, when key material
119119arrives in a protocol message and is used only once - most cryptographic
@@ -124,46 +124,31 @@ options accepted by each format.
124124
125125#### KeyObject
126126
127- A [ ` KeyObject ` ] [ ] is the in-memory representation of a parsed key and is
128- ** the preferred way to work with keys** in ` node:crypto ` . It is created by
129- [ ` crypto.createPublicKey() ` ] [ ] , [ ` crypto.createPrivateKey() ` ] [ ] ,
127+ A [ ` KeyObject ` ] [ ] is the in-memory representation of a parsed key. It is
128+ created by [ ` crypto.createPublicKey() ` ] [ ] , [ ` crypto.createPrivateKey() ` ] [ ] ,
130129[ ` crypto.createSecretKey() ` ] [ ] , or key generation functions such as
131- [ ` crypto.generateKeyPair() ` ] [ ] .
132-
133- Because the key material is parsed once at creation time, reusing a
134- [ ` KeyObject ` ] [ ] across multiple operations avoids repeated parsing and
135- delivers the best performance - for example, each Ed25519 signing operation
136- with a reused [ ` KeyObject ` ] [ ] is over 2× faster than passing a PEM string,
137- and the savings compound with every subsequent use of the same [ ` KeyObject ` ] [ ] .
138- Always prefer creating a [ ` KeyObject ` ] [ ] up front when the same key is used
139- more than once. The first cryptographic operation with a given
130+ [ ` crypto.generateKeyPair() ` ] [ ] . The first cryptographic operation with a given
140131[ ` KeyObject ` ] [ ] may be slower than subsequent ones because OpenSSL lazily
141- initializes internal caches on first use, but it will still generally be faster
142- than passing key material in any other format.
132+ initializes internal caches on first use.
143133
144134#### PEM and DER
145135
146136PEM and DER are the traditional encoding formats for asymmetric keys based on
147- ASN.1 structures. For private keys, these structures typically carry both the
148- private and public key components, so no additional computation is needed
149- during import - however, the ASN.1 parsing itself is the main cost.
137+ ASN.1 structures.
150138
151139* ** PEM** is a text encoding that wraps Base64-encoded DER data between
152140 header and footer lines (e.g. ` -----BEGIN PUBLIC KEY----- ` ). PEM strings can
153- be passed directly to most cryptographic operations. Public keys typically use
154- the ` 'spki' ` type and private keys typically use ` 'pkcs8' ` .
141+ be passed directly to most cryptographic operations.
155142* ** DER** is the binary encoding of the same ASN.1 structures. When providing
156143 DER input, the ` type ` (typically ` 'spki' ` or ` 'pkcs8' ` ) must be specified
157- explicitly. DER avoids the Base64 decoding overhead of PEM, and the explicit
158- ` type ` lets the parser skip type detection, making DER slightly faster to
159- import than PEM.
144+ explicitly.
160145
161146#### JSON Web Key (JWK)
162147
163148JSON Web Key (JWK) is a JSON-based key representation defined in
164- [ RFC 7517] [ ] . Instead of wrapping key material in ASN.1 structures, JWK
165- encodes each key component as an individual Base64url-encoded value inside a
166- JSON object .
149+ [ RFC 7517] [ ] . JWK encodes each key component as an individual Base64url-encoded
150+ value inside a JSON object. For RSA keys, JWK avoids ASN.1 parsing overhead
151+ and is the fastest serialized import format .
167152
168153#### Raw key formats
169154
@@ -174,15 +159,12 @@ importing and exporting raw key material without any encoding wrapper.
174159See [ ` keyObject.export() ` ] [ ] , [ ` crypto.createPublicKey() ` ] [ ] , and
175160[ ` crypto.createPrivateKey() ` ] [ ] for usage details.
176161
177- The ` 'raw-public' ` format is generally the fastest way to import a public key
178- because no ASN.1 or Base64 decoding is needed. The ` 'raw-private' ` and
179- ` 'raw-seed' ` formats, however, are not always faster than PEM, DER, or JWK
180- because those formats only contain the private scalar or seed - importing them
181- requires mathematically deriving the public key component for the purpose of
182- committing the public key to the cryptographic output (e.g. elliptic curve
183- point multiplication or seed expansion), which can be expensive depending on
184- the key type. Other formats like PEM, DER, or JWK include both private and
185- public components, avoiding that computation.
162+ ` 'raw-public' ` is generally the fastest way to import a public key.
163+ ` 'raw-private' ` and ` 'raw-seed' ` are not always faster than other formats
164+ because they only contain the private scalar or seed - importing them requires
165+ deriving the public key component (e.g. elliptic curve point multiplication or
166+ seed expansion), which can be expensive. Other formats include both private
167+ and public components, avoiding that computation.
186168
187169### Choosing a key format
188170
@@ -203,8 +185,7 @@ or expanding a seed). Which part dominates depends on the key type. For
203185example:
204186
205187* Public keys - ` 'raw-public' ` is the fastest serialized format because the
206- raw format skips all ASN.1 and Base64 decoding. For Ed25519 public key
207- import, ` 'raw-public' ` can be over 2× faster than PEM.
188+ raw format skips all ASN.1 and Base64 decoding.
208189* EC private keys - ` 'raw-private' ` is faster than PEM or DER because it
209190 avoids ASN.1 parsing. However, for larger curves (e.g. P-384, P-521) the
210191 required derivation of the public point from the private scalar becomes
@@ -229,7 +210,7 @@ signing or verification, the import cost is a larger fraction of the total,
229210so a faster format like ` 'raw-public' ` or ` 'raw-private' ` can meaningfully
230211improve throughput.
231212
232- If the same key material is used only a few times, it is worth importing it
213+ Even if the same key material is used only a few times, it is worth importing it
233214into a [ ` KeyObject ` ] [ ] rather than passing the raw or PEM representation
234215repeatedly.
235216
@@ -250,174 +231,77 @@ const signature = sign(null, data, privateKey);
250231verify (null , data, publicKey, signature);
251232```
252233
253- Example: Importing a PEM-encoded key into a [ ` KeyObject ` ] [ ] :
234+ Example: Importing keys of various formats into [ ` KeyObject ` ] [ ] s :
254235
255236``` mjs
256237import { promisify } from ' node:util' ;
257238const {
258- createPrivateKey , createPublicKey , generateKeyPair , sign , verify ,
239+ createPrivateKey , createPublicKey , generateKeyPair ,
259240} = await import (' node:crypto' );
260241
261- // PEM-encoded keys, e.g. read from a file or environment variable.
262242const generated = await promisify (generateKeyPair)(' ed25519' );
243+
244+ // PEM
263245const privatePem = generated .privateKey .export ({ format: ' pem' , type: ' pkcs8' });
264246const publicPem = generated .publicKey .export ({ format: ' pem' , type: ' spki' });
247+ createPrivateKey (privatePem);
248+ createPublicKey (publicPem);
265249
266- const privateKey = createPrivateKey (privatePem);
267- const publicKey = createPublicKey (publicPem);
268-
269- const data = new TextEncoder ().encode (' message to sign' );
270- const signature = sign (null , data, privateKey);
271- verify (null , data, publicKey, signature);
272- ```
273-
274- Example: Importing a JWK into a [ ` KeyObject ` ] [ ] :
275-
276- ``` mjs
277- import { promisify } from ' node:util' ;
278- const {
279- createPrivateKey , createPublicKey , generateKeyPair , sign , verify ,
280- } = await import (' node:crypto' );
250+ // DER - requires explicit type
251+ const privateDer = generated .privateKey .export ({ format: ' der' , type: ' pkcs8' });
252+ const publicDer = generated .publicKey .export ({ format: ' der' , type: ' spki' });
253+ createPrivateKey ({ key: privateDer, format: ' der' , type: ' pkcs8' });
254+ createPublicKey ({ key: publicDer, format: ' der' , type: ' spki' });
281255
282- // JWK objects, e.g. from a JSON configuration or API response.
283- const generated = await promisify (generateKeyPair)(' ed25519' );
256+ // JWK
284257const privateJwk = generated .privateKey .export ({ format: ' jwk' });
285258const publicJwk = generated .publicKey .export ({ format: ' jwk' });
259+ createPrivateKey ({ key: privateJwk, format: ' jwk' });
260+ createPublicKey ({ key: publicJwk, format: ' jwk' });
286261
287- const privateKey = createPrivateKey ({ key: privateJwk, format: ' jwk' });
288- const publicKey = createPublicKey ({ key: publicJwk, format: ' jwk' });
289-
290- const data = new TextEncoder ().encode (' message to sign' );
291- const signature = sign (null , data, privateKey);
292- verify (null , data, publicKey, signature);
262+ // Raw
263+ const rawPriv = generated .privateKey .export ({ format: ' raw-private' });
264+ const rawPub = generated .publicKey .export ({ format: ' raw-public' });
265+ createPrivateKey ({ key: rawPriv, format: ' raw-private' , asymmetricKeyType: ' ed25519' });
266+ createPublicKey ({ key: rawPub, format: ' raw-public' , asymmetricKeyType: ' ed25519' });
293267```
294268
295- Example: Importing a DER-encoded key into a [ ` KeyObject ` ] [ ] :
269+ Example: Passing key material directly to [ ` crypto.sign() ` ] [ ] and
270+ [ ` crypto.verify() ` ] [ ] without creating a [ ` KeyObject ` ] [ ] first:
296271
297272``` mjs
298273import { promisify } from ' node:util' ;
299- const {
300- createPrivateKey , createPublicKey , generateKeyPair , sign , verify ,
301- } = await import (' node:crypto' );
274+ const { generateKeyPair , sign , verify } = await import (' node:crypto' );
302275
303- // DER-encoded keys, e.g. read from binary files or hex/base64url-decoded
304- // from environment variables.
305276const generated = await promisify (generateKeyPair)(' ed25519' );
306- const privateDer = generated .privateKey .export ({ format: ' der' , type: ' pkcs8' });
307- const publicDer = generated .publicKey .export ({ format: ' der' , type: ' spki' });
308-
309- const privateKey = createPrivateKey ({
310- key: privateDer,
311- format: ' der' ,
312- type: ' pkcs8' ,
313- });
314- const publicKey = createPublicKey ({
315- key: publicDer,
316- format: ' der' ,
317- type: ' spki' ,
318- });
319277
320278const data = new TextEncoder ().encode (' message to sign' );
321- const signature = sign (null , data, privateKey);
322- verify (null , data, publicKey, signature);
323- ```
324-
325- Example: Passing PEM strings directly to [ ` crypto.sign() ` ] [ ] and
326- [ ` crypto.verify() ` ] [ ] :
327279
328- ``` mjs
329- import { promisify } from ' node:util' ;
330- const { generateKeyPair , sign , verify } = await import (' node:crypto' );
331-
332- const generated = await promisify (generateKeyPair)(' ed25519' );
280+ // PEM strings
333281const privatePem = generated .privateKey .export ({ format: ' pem' , type: ' pkcs8' });
334282const publicPem = generated .publicKey .export ({ format: ' pem' , type: ' spki' });
283+ const sig1 = sign (null , data, privatePem);
284+ verify (null , data, publicPem, sig1);
335285
336- // PEM strings can be passed directly without creating a KeyObject first.
337- const data = new TextEncoder ().encode (' message to sign' );
338- const signature = sign (null , data, privatePem);
339- verify (null , data, publicPem, signature);
340- ```
341-
342- Example: Passing JWK objects directly to [ ` crypto.sign() ` ] [ ] and
343- [ ` crypto.verify() ` ] [ ] :
344-
345- ``` mjs
346- import { promisify } from ' node:util' ;
347- const { generateKeyPair , sign , verify } = await import (' node:crypto' );
348-
349- const generated = await promisify (generateKeyPair)(' ed25519' );
286+ // JWK objects
350287const privateJwk = generated .privateKey .export ({ format: ' jwk' });
351288const publicJwk = generated .publicKey .export ({ format: ' jwk' });
352-
353- // JWK objects can be passed directly without creating a KeyObject first.
354- const data = new TextEncoder ().encode (' message to sign' );
355- const signature = sign (null , data, { key: privateJwk, format: ' jwk' });
356- verify (null , data, { key: publicJwk, format: ' jwk' }, signature);
357- ```
358-
359- Example: Passing raw key bytes directly to [ ` crypto.sign() ` ] [ ] and
360- [ ` crypto.verify() ` ] [ ] :
361-
362- ``` mjs
363- import { promisify } from ' node:util' ;
364- const { generateKeyPair , sign , verify } = await import (' node:crypto' );
365-
366- const generated = await promisify (generateKeyPair)(' ed25519' );
367- const rawPrivateKey = generated .privateKey .export ({ format: ' raw-private' });
368- const rawPublicKey = generated .publicKey .export ({ format: ' raw-public' });
369-
370- // Raw key bytes can be passed directly without creating a KeyObject first.
371- const data = new TextEncoder ().encode (' message to sign' );
372- const signature = sign (null , data, {
373- key: rawPrivateKey,
374- format: ' raw-private' ,
375- asymmetricKeyType: ' ed25519' ,
289+ const sig2 = sign (null , data, { key: privateJwk, format: ' jwk' });
290+ verify (null , data, { key: publicJwk, format: ' jwk' }, sig2);
291+
292+ // Raw key bytes
293+ const rawPriv = generated .privateKey .export ({ format: ' raw-private' });
294+ const rawPub = generated .publicKey .export ({ format: ' raw-public' });
295+ const sig3 = sign (null , data, {
296+ key: rawPriv, format: ' raw-private' , asymmetricKeyType: ' ed25519' ,
376297});
377298verify (null , data, {
378- key: rawPublicKey,
379- format: ' raw-public' ,
380- asymmetricKeyType: ' ed25519' ,
381- }, signature);
382- ```
383-
384- Example: Exporting raw keys and importing them:
385-
386- ``` mjs
387- import { promisify } from ' node:util' ;
388- const {
389- createPrivateKey , createPublicKey , generateKeyPair , sign , verify ,
390- } = await import (' node:crypto' );
391-
392- const generated = await promisify (generateKeyPair)(' ed25519' );
393-
394- // Export the raw public key (32 bytes for Ed25519).
395- const rawPublicKey = generated .publicKey .export ({ format: ' raw-public' });
396-
397- // Export the raw private key (32 bytes for Ed25519).
398- const rawPrivateKey = generated .privateKey .export ({ format: ' raw-private' });
399-
400- // Import the raw public key.
401- const publicKey = createPublicKey ({
402- key: rawPublicKey,
403- format: ' raw-public' ,
404- asymmetricKeyType: ' ed25519' ,
405- });
406-
407- // Import the raw private key.
408- const privateKey = createPrivateKey ({
409- key: rawPrivateKey,
410- format: ' raw-private' ,
411- asymmetricKeyType: ' ed25519' ,
412- });
413-
414- const data = new TextEncoder ().encode (' message to sign' );
415- const signature = sign (null , data, privateKey);
416- verify (null , data, publicKey, signature);
299+ key: rawPub, format: ' raw-public' , asymmetricKeyType: ' ed25519' ,
300+ }, sig3);
417301```
418302
419303Example: For EC keys, the ` namedCurve ` option is required when importing
420- ` ' raw-public' ` keys:
304+ raw keys:
421305
422306``` mjs
423307import { promisify } from ' node:util' ;
0 commit comments