crypto.generateKeyPairSync()

Generates a matching public and private key pair synchronously, as KeyObjects or encoded PEM/DER.

Signature
crypto.generateKeyPairSync(type, options)
  • Added in Node.js v11.6.0

Parameters

ParameterTypeDescription
$typestringThe intended use of the generated key pair. Must be 'rsa', 'rsa-pss', 'dsa', 'ec', 'ed25519', 'ed448', 'x25519', 'x448', or 'dh'.
$optionsObjectAlgorithm parameters plus the optional output encodings. Which fields apply depends on `type`.
$options.modulusLengthoptionalnumberKey size in bits (RSA, RSA-PSS).
$options.publicExponentoptionalnumber= 0x10001Public exponent (RSA, RSA-PSS).
$options.mgf1HashAlgorithmoptionalstringMGF1 hash algorithm (RSA-PSS).
$options.saltLengthoptionalnumberSalt length (RSA-PSS).
$options.divisorLengthoptionalnumberSize of q in bits (DSA).
$options.namedCurveoptionalstringNamed curve (EC). Examples: 'prime256v1', 'secp384r1', 'secp256k1'.
$options.publicKeyEncodingoptionalObjectWhen present, the public key is returned encoded rather than as a KeyObject.
$options.publicKeyEncoding.typestringMust be 'spki' or 'pkcs1'.
$options.publicKeyEncoding.formatstringMust be 'pem' or 'der'.
$options.privateKeyEncodingoptionalObjectWhen present, the private key is returned encoded rather than as a KeyObject.
$options.privateKeyEncoding.typestringMust be 'pkcs1', 'pkcs8', or 'sec1'.
$options.privateKeyEncoding.formatstringMust be 'pem' or 'der'.
$options.privateKeyEncoding.cipheroptionalstringIf specified, the private key is encrypted with this cipher and the passphrase using PKCS#5 v2.0 password based encryption.
$options.privateKeyEncoding.passphraseoptionalstring | BufferThe passphrase to use for encryption.

Return value

ObjectAn object with `publicKey` and `privateKey`. Each is a `KeyObject` unless a matching `publicKeyEncoding` or `privateKeyEncoding` was supplied, in which case that key is returned as a PEM string or a DER `Buffer`.

Errors

Throws ERR_INVALID_ARG_VALUE for an unknown key type, and ERR_INCOMPATIBLE_OPTION_PAIR or ERR_MISSING_OPTION when required algorithm options such as modulusLength or namedCurve are absent or contradictory.

Changelog

  • PHP v12.0.0The returned keys are now KeyObject instances instead of raw strings when no encoding is given.
  • PHP v13.9.0Added support for the 'ed25519' and 'ed448' key types.
  • PHP v15.7.0Added support for the 'rsa-pss' key type.
  • PHP v17.5.0RSA-PSS key padding can be disabled by passing saltLength: -1.
  • PHP v17.7.0Added support for the 'dh' key type.
  • PHP v18.12.0Added support for the 'ed448', 'x25519', and 'x448' key types.
  • PHP v26.0.0Added the post-quantum 'ml-dsa-*', 'ml-kem-*' and 'slh-dsa-*' key types.

This is the synchronous twin of crypto.generateKeyPair(). It blocks the event loop for the whole generation, which is why the choice of type matters more than anything else on this page: an Ed25519 or P-256 pair appears in well under a millisecond, while a 2048-bit RSA pair can take hundreds of milliseconds and a 4096-bit pair several seconds. Blocking a request handler for that long is the classic misuse.

KeyObject or text

With no encoding options you get KeyObject instances. Keep them that way when the key never leaves the process — they carry the type and curve with them, cannot be accidentally logged as a secret string, and are accepted directly by createSign(), createVerify() and the one-shot crypto.sign(). Add publicKeyEncoding or privateKeyEncoding only when the key must be written to a file, an environment variable, or sent to another system. The two encodings are independent: exporting the public key as SPKI PEM while keeping the private key as a KeyObject is a good default.

Picking a type

  • 'ed25519' — signatures. Fastest to generate, no options needed, fixed 64-byte signatures.
  • 'ec' with namedCurve: 'prime256v1' — signatures where a peer expects ECDSA.
  • 'rsa' with modulusLength: 2048 — only when something on the other end demands RSA.
  • 'x25519' — key agreement, not signing.

Generate at startup or in a script, never per request.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Create a signing key pair at service startup for issuing internal tokens.
  • Write a setup script that emits an SPKI public key PEM to hand to a partner and a passphrase-encrypted PKCS#8 private key to disk.
  • Generate a throwaway P-256 pair inside a test so fixtures never ship a real key.
  • Produce an X25519 pair for an ECDH key agreement handshake.

Frequently asked questions

Why is crypto.generateKeyPairSync slow?

Because RSA generation has to search for large random primes, and the synchronous form blocks the event loop while it does. Switch to 'ed25519' or an EC curve, which generate in microseconds, or use the callback-based crypto.generateKeyPair() which runs on the thread pool.

How do I get a PEM string instead of a KeyObject from generateKeyPairSync?

Pass publicKeyEncoding and privateKeyEncoding, for example { type: 'spki', format: 'pem' } and { type: 'pkcs8', format: 'pem' }. Each encoding is independent, so you can export one key as text and keep the other as a KeyObject.

Which key type should I use for signing in Node.js?

Use 'ed25519' unless a peer requires otherwise: generation is instant, signatures are a fixed 64 bytes, and there are no options to get wrong. Sign with crypto.sign() rather than createSign(), which does not support Ed25519.

More examples

Encrypt the private key with a passphrase

Adding cipher and passphrase to privateKeyEncoding changes the PEM banner from BEGIN PRIVATE KEY to BEGIN ENCRYPTED PRIVATE KEY, so the file is useless without the passphrase. The output shows the encrypted banner and then ed25519, proving the key was decrypted and parsed back into a KeyObject.

Example · javascript
const { generateKeyPairSync, createPrivateKey } = require('node:crypto');

const { publicKey, privateKey } = generateKeyPairSync('ed25519', {
  publicKeyEncoding: { type: 'spki', format: 'pem' },
  privateKeyEncoding: {
    type: 'pkcs8',
    format: 'pem',
    cipher: 'aes-256-cbc',
    passphrase: process.env.KEY_PASSPHRASE ?? 'change-me',
  },
});

console.log(privateKey.split('\n')[0]);
// -----BEGIN ENCRYPTED PRIVATE KEY-----

// Loading it back requires the same passphrase.
const key = createPrivateKey({
  key: privateKey,
  format: 'pem',
  passphrase: process.env.KEY_PASSPHRASE ?? 'change-me',
});
console.log('loaded:', key.asymmetricKeyType);

RSA-2048 with an explicit exponent, timed

asymmetricKeyDetails reports the parameters the key was actually built with, which is the reliable way to confirm modulusLength took effect. The elapsed time is reported as a boolean rather than a number because RSA generation searches for primes and the duration varies wildly between runs on the same machine.

Example · javascript
const { generateKeyPairSync } = require('node:crypto');

const started = process.hrtime.bigint();
const { privateKey } = generateKeyPairSync('rsa', {
  modulusLength: 2048,
  publicExponent: 0x10001, // the default, spelled out
});
const ms = Number(process.hrtime.bigint() - started) / 1e6;

console.log('type:', privateKey.asymmetricKeyType);
console.log('modulus bits:', privateKey.asymmetricKeyDetails.modulusLength);
console.log('took under a second:', ms < 1000);

Export a public key as DER bytes for a binary protocol

Only publicKeyEncoding was supplied, so the public key comes back as a 91-byte DER Buffer while the private key remains a KeyObject that never becomes a string. The DER length for a P-256 SPKI key is fixed, which makes it a useful sanity check when framing keys into a binary wire format.

Example · javascript
const { generateKeyPairSync } = require('node:crypto');

const { publicKey, privateKey } = generateKeyPairSync('ec', {
  namedCurve: 'prime256v1',
  publicKeyEncoding: { type: 'spki', format: 'der' },
});

console.log('publicKey is a Buffer:', Buffer.isBuffer(publicKey));
console.log('P-256 SPKI DER length:', publicKey.length); // 91
console.log('privateKey stayed a KeyObject:', privateKey.type);

// DER is just the binary form of the same key.
console.log('as base64:', publicKey.toString('base64').slice(0, 24) + '...');

Discussion

  • Be the first to comment on this lesson.