crypto.generateKeyPairSync()
Generates a matching public and private key pair synchronously, as KeyObjects or encoded PEM/DER.
crypto.generateKeyPairSync(type, options)- Added in Node.js v11.6.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$type | string | The intended use of the generated key pair. Must be 'rsa', 'rsa-pss', 'dsa', 'ec', 'ed25519', 'ed448', 'x25519', 'x448', or 'dh'. |
$options | Object | Algorithm parameters plus the optional output encodings. Which fields apply depends on `type`. |
$options.modulusLengthoptional | number | Key size in bits (RSA, RSA-PSS). |
$options.publicExponentoptional | number= 0x10001 | Public exponent (RSA, RSA-PSS). |
$options.mgf1HashAlgorithmoptional | string | MGF1 hash algorithm (RSA-PSS). |
$options.saltLengthoptional | number | Salt length (RSA-PSS). |
$options.divisorLengthoptional | number | Size of q in bits (DSA). |
$options.namedCurveoptional | string | Named curve (EC). Examples: 'prime256v1', 'secp384r1', 'secp256k1'. |
$options.publicKeyEncodingoptional | Object | When present, the public key is returned encoded rather than as a KeyObject. |
$options.publicKeyEncoding.type | string | Must be 'spki' or 'pkcs1'. |
$options.publicKeyEncoding.format | string | Must be 'pem' or 'der'. |
$options.privateKeyEncodingoptional | Object | When present, the private key is returned encoded rather than as a KeyObject. |
$options.privateKeyEncoding.type | string | Must be 'pkcs1', 'pkcs8', or 'sec1'. |
$options.privateKeyEncoding.format | string | Must be 'pem' or 'der'. |
$options.privateKeyEncoding.cipheroptional | string | If specified, the private key is encrypted with this cipher and the passphrase using PKCS#5 v2.0 password based encryption. |
$options.privateKeyEncoding.passphraseoptional | string | Buffer | The 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'withnamedCurve: 'prime256v1'— signatures where a peer expects ECDSA.'rsa'withmodulusLength: 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
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.
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.
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.
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