Skip to Content
@exortek/jwkimport — JWK / PEM → KeyObject

jwk.import / jwk.importPEM

Turn key material (a JWK object, an SPKI / PKCS#8 PEM, an X.509 certificate, or their DER equivalents) into a node:crypto.KeyObject — the shape jws, jwt, and jwe will expect downstream.

import { jwk } from '@exortek/jwk' // JWK → KeyObject const key = await jwk.import(privateJwk) // PEM → KeyObject const publicKey = await jwk.importPEM(spkiPem) // 'spki' default const privateKey = await jwk.importPEM(pkcs8Pem, 'pkcs8') const fromCert = await jwk.importPEM(x509CertPem, 'x509') // extracts public key

The named exports importJWK and importPEM are single-purpose; the jwk namespace exposes jwk.import as an alias for importJWK.

importJWK(jwk, options?)

Runs validate first, then hands the JWK to createPrivateKey / createPublicKey / createSecretKey per kty.

Input kindResult
Public JWK (no d)KeyObject { type: 'public' }
Private JWK (d)KeyObject { type: 'private' }
kty: 'oct'KeyObject { type: 'secret' }

Everything the JWK carries — kid / use / alg / key_ops — is already validated. Node ignores JWK metadata during import; retain the JWK itself if you need those fields later.

importPEM(pemOrDer, format='spki')

Explicit format selection — this is the surface for interop with openssl, ssh-keygen, KMS providers, and X.509 certificate authorities.

formatMeaningResult
'spki'SubjectPublicKeyInfo (public)type: 'public'
'pkcs8'PrivateKeyInfo (private)type: 'private'
'x509'X.509 certificate; public key liftedtype: 'public'

Input encoding. Strings are treated as PEM; Buffer is treated as DER. The same call handles both.

// PEM string const key = await jwk.importPEM(spkiPemString) // DER buffer const key2 = await jwk.importPEM(derBuffer)

There is deliberately no 'sec1' or 'pkcs1' format — SEC1 and PKCS#1 encode either public or private keys depending on internal fields, which would leave the shape ambiguous. Convert those to SPKI / PKCS#8 once at the source (openssl pkcs8 -topk8 -in sec1.pem -out pkcs8.pem) and use importPEM from there.

Round-tripping

const spkiPem = await jwk.exportPEM(publicKey) // 'spki' by default const back = await jwk.importPEM(spkiPem) // 'spki' by default // back is a fresh KeyObject with equivalent material.

For JWK ↔ JWK trips through a KeyObject, metadata (kid / use / alg) is stripped by Node — pass it back explicitly on exportJWK:

const key = await jwk.import(privateJwk) const back = await jwk.export(key, { kid: privateJwk.kid, use: privateJwk.use, alg: privateJwk.alg, })

Errors

CodeWhen
INVALID_JWKThe JWK failed validate (shape / coord length / secret-carrying mismatch).
UNSUPPORTED_KTYkty not one of the four supported.
UNSUPPORTED_CURVEcrv outside the per-kty matrix.
KEY_OPS_CONFLICTuse and key_ops inconsistent per RFC 7517 §4.3.
INVALID_KEYnode:crypto rejected the JWK / PEM / DER after validation passed.
INVALID_FORMATimportPEM given a format other than 'spki' / 'pkcs8' / 'x509'.
INVALID_ARGUMENTimportPEM given non-string, non-Buffer input.
Last updated on