DEV Community

ntoledo319
ntoledo319

Posted on • Originally published at eolkits.com

"error:1E08010C:DECODER routines::unsupported — why an old PEM key breaks after a Lambda Node.js upgrade"

You bump the Lambda runtime label from nodejs16.x to nodejs18.x, 20.x, or 22.x. The handler code doesn't change. The private key you've been loading for months — for signing a JWT, terminating TLS, or calling crypto.createPrivateKey — suddenly throws:

Error: error:1E08010C:DECODER routines::unsupported
    at new Sign (node:internal/crypto/sig:112:19)
    at Object.createPrivateKey (node:crypto:...)
Enter fullscreen mode Exit fullscreen mode

No code changed. The runtime did. Here's why.

The cause in two sentences

Node.js 17 and later bundle OpenSSL 3, and OpenSSL 3 disables a set of legacy key encodings and ciphers by default. If your private key is stored in an older format — a traditional PKCS#1 PEM, or a PEM encrypted with a weak/legacy cipher — OpenSSL 3's default provider refuses to decode it, and Node surfaces that refusal as DECODER routines::unsupported.

Why this hits exactly at runtime-upgrade time

Lambda's Node.js runtimes map straight to OpenSSL versions:

Lambda runtime Node.js OpenSSL Legacy PKCS#1 key load
nodejs16.x 16.x 1.1.1 Works
nodejs18.x 18.x 3.0 Fails
nodejs20.x 20.x 3.0 Fails
nodejs22.x 22.x 3.0 Fails

nodejs16.x is on the deprecation path (block-create Feb 1 2027 / block-update Mar 3 2027, per AWS's Lambda runtime deprecation table). Every team moving off it lands on an OpenSSL-3 runtime and inherits this the first time the function touches an old-format key — JWT signing, an mTLS client cert, an SSH-style key stored as a secret.

Fix 1 — re-encode the key to PKCS#8 (durable)

PKCS#8 is the modern, fully-supported container format. Converting is a one-time operation, not a runtime workaround:

# Unencrypted key
openssl pkcs8 -topk8 -nocrypt -in old-pkcs1.pem -out new-pkcs8.pem

# Keep it encrypted
openssl pkcs8 -topk8 -in old-pkcs1.pem -out new-pkcs8.pem
Enter fullscreen mode Exit fullscreen mode

If the key itself uses a legacy cipher rather than just a legacy container:

openssl rsa -in old.pem -out new.pem
Enter fullscreen mode Exit fullscreen mode

Store new-pkcs8.pem wherever the old key lived — Lambda environment variable, Secrets Manager, or Parameter Store — and point your code at it. No application code changes; crypto.createPrivateKey reads PKCS#8 natively.

Fix 2 — the legacy provider flag (stopgap only)

If you can't rotate the key today:

node --openssl-legacy-provider index.js
Enter fullscreen mode Exit fullscreen mode

Or as a Lambda environment variable:

NODE_OPTIONS=--openssl-legacy-provider
Enter fullscreen mode Exit fullscreen mode

This re-enables OpenSSL's legacy algorithm set for the entire process — not just key loading. It weakens the runtime's default crypto posture, and the flag itself is not guaranteed to survive future OpenSSL major bumps. Treat it as a bridge to Fix 1, not a destination.

Confirm the key format before you deploy

openssl asn1parse -in your-key.pem | head -5
Enter fullscreen mode Exit fullscreen mode

PKCS#1 RSA PRIVATE KEY in the header line means you have the legacy format; PRIVATE KEY (no algorithm named) means it's already PKCS#8 and this error isn't about this key.

The pattern to internalize

DECODER routines::unsupported is a credential-age signal, the same way digital envelope routines::unsupported is a build-tooling-age signal. Both come from OpenSSL 3 refusing something OpenSSL 1.1 allowed. If a Lambda function has been signing or decrypting with the same key since it was created on nodejs12.x or nodejs14.x, a runtime upgrade is the moment to rotate it into a modern format — not just patch around the error.


Not sure which of your Lambda functions still load a legacy-format key, or which runtimes they're on relative to AWS's actual block dates? Run a free EOLkits scan — it maps every function to its runtime deprecation date and flags native-dependency and crypto-compatibility issues before AWS does.

Top comments (0)