n8n-io/n8n · error · Error

Certificate authentication requires an RSA private key

Error message

Certificate authentication requires an RSA private key

What it means

Thrown by buildClientAssertion when the parsed private key's asymmetricKeyType is not 'rsa'. The signer pins the JWT alg header to RS256 and uses createSign('RSA-SHA256'); an EC or Ed25519 key would produce a signature that contradicts the declared alg, so non-RSA keys are rejected up front to prevent an invalid token.

Source

Thrown at packages/@n8n/utils/src/client-assertion.ts:70

		iat: now,
		nbf: now,
		exp: now + ASSERTION_TTL_SECONDS,
	};

	let privateKey: KeyObject;
	try {
		privateKey = createPrivateKey(formatPemBlock(options.privateKey));
	} catch (error) {
		throw new Error(
			'The Private Key field must contain a PEM private key (-----BEGIN PRIVATE KEY-----).',
			{ cause: error },
		);
	}

	// `createSign('RSA-SHA256')` also signs EC/Ed25519 keys, producing a signature
	// that contradicts the pinned `alg: RS256` header. Reject non-RSA keys up front.
	if (privateKey.asymmetricKeyType !== 'rsa') {
		throw new Error('Certificate authentication requires an RSA private key');
	}

	const signingInput = `${base64url(JSON.stringify(header))}.${base64url(JSON.stringify(payload))}`;
	const signature = createSign('RSA-SHA256').update(signingInput).sign(privateKey);
	return `${signingInput}.${base64url(signature)}`;
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Generate an RSA private key: `openssl genrsa -out private.pem 2048` (or 3072/4096).
  2. If you must use an EC key, switch to a signer that supports ES256 and updates the alg header accordingly (this function does not).
  3. Verify the key type at config time: createPrivateKey(pem).asymmetricKeyType === 'rsa'.

Example fix

// before
// generated: openssl genpkey -algorithm ED25519 -out private.pem
buildClientAssertion({ ..., privateKey: ed25519Pem }); // throws
// after
// generated: openssl genrsa -out private.pem 2048
buildClientAssertion({ ..., privateKey: rsaPem });
Defensive patterns

Strategy: validation

Validate before calling

import { createPrivateKey } from 'node:crypto';

function assertRsaPrivateKey(key: string): void {
  const ko = createPrivateKey(key);
  if (ko.asymmetricKeyType !== 'rsa') {
    throw new Error(`Expected an RSA private key, got '${ko.asymmetricKeyType}'. Generate with: openssl genrsa -out private.pem 2048`);
  }
}
// then call before buildClientAssertion.

Type guard

import { createPrivateKey, type KeyObject } from 'node:crypto';

function isRsaPrivateKey(pem: string): boolean {
  try {
    return createPrivateKey(pem).asymmetricKeyType === 'rsa';
  } catch {
    return false;
  }
}

Try / catch

try {
  return buildClientAssertion(opts);
} catch (e) {
  if (/requires an RSA private key/i.test(e?.message ?? '')) {
    throw new ConfigError('Certificate authentication requires an RSA key; regenerate with openssl genrsa.', { cause: e });
  }
  throw e;
}

Prevention

When it happens

Trigger: Generating an ECDSA P-256 or Ed25519 keypair for OAuth client authentication and supplying the EC/Ed private key. Using a key from a vault/KMS that defaults to EC instead of RSA.

Common situations: Modern key-generation defaults (openssl ecparam, ssh-keygen -t ed25519) producing non-RSA keys; security policies mandating EC keys that conflict with this RS256-only signer; misreading the API doc that states RS256-only.

Understand the failure class

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/0e54cbf476dcae53. Report an issue: GitHub.