Skip to content
Effect 4.0 is here. One ecosystem. Zero dependencies.

KeyFormat unstable

Key formats: SPKI, PKCS#8, JWK, and PEM, with import-only PKCS#1 and SEC1.

decodePublic, decodePrivate, and decodeSecret build typed keys from encoded data, and encode writes a key in a named format. They are plain functions, not a service: each returns an Effect that requires nothing and runs on the WebCrypto reference.

The algorithm decides the family of the result. Decoding with Signature.ecdsaP256 gives a Signature key, and with KeyAgreement.ecdhP256 a KeyAgreement key. Raw bytes are not a format here: each family's own constructors read them.

Format values carry their role, direction, and data type in the types:

  • spki (public) and pkcs8 (private) read and write DER bytes;
  • jwk reads and writes JWK objects for every role;
  • pkcs1 (RSA) and sec1 (EC) read private DER keys and cannot be written;
  • pem turns any DER format into PEM text.

Decoded keys always hold bytes, in the forms each family stores, so they work with every layer.

Gotchas

  • EC private keys must include their public point. Safari and Firefox before 132 cannot import one without it, so every runtime fails with an Unsupported reason.
  • SPKI accepts compressed EC points. Safari and Firefox before 146 reject them in raw import, so the families' raw constructors take only uncompressed points.
  • Ed25519 and X25519 private JWKs need a correct x. Bun and Deno accept d alone, and other runtimes reject it.
  • JWK members other than kty, crv, alg, and the key material are ignored on import, and written JWKs contain only kty, crv, and the key material.
  • Callers parse and serialize JSON themselves.
20 exports Since v4.0.0 Source

Decoding

Since v4.0.0

Decodes a private key.

Details

The format must hold private keys: pkcs8, jwk, pkcs1, sec1, or their PEM forms. PKCS#1 and SEC1 are converted to PKCS#8 before the import. Malformed data, a key of another algorithm or curve, or a JWK without d fails with an InvalidKey reason. An EC private key without its public point fails with an Unsupported reason on every runtime, because Safari and Firefox before 132 cannot import it.

Signature

declare function decodePrivate<A extends AsymmetricAlgorithm, Data>(format: Format<"private", "decode", Data>, algorithm: A, data: NoInfer<Data>): Effect<PrivateKey<A>, CryptoError>
Since v4.0.0

Decodes a public key.

Details

The format must hold public keys: spki, jwk, or pem(spki). The key is imported on the WebCrypto reference and stored as bytes. Malformed data, a key of another algorithm or curve, or a JWK with d fails with an InvalidKey reason.

Signature

declare function decodePublic<A extends AsymmetricAlgorithm, Data>(format: Format<"public", "decode", Data>, algorithm: A, data: NoInfer<Data>): Effect<PublicKey<A>, CryptoError>

Example Reading a PEM public key

import { Effect } from "effect"
import { KeyFormat, Signature } from "effect/crypto"
declare const text: string
const publicKey = KeyFormat.decodePublic(KeyFormat.pem(KeyFormat.spki), Signature.ecdsaP256, text)
Since v4.0.0

Decodes a secret key from a JWK.

Details

The JWK must have kty oct when kty is present, and an alg matching the algorithm when alg is present. The key bytes go through the family's raw constructor, so a wrong length fails with an InvalidKey reason. Secret keys never touch crypto.subtle here.

Signature

declare function decodeSecret<A extends SecretAlgorithm, Data>(format: Format<"secret", "decode", Data>, algorithm: A, data: NoInfer<Data>): Effect<SecretKey<A>, CryptoError>

Encoding

encode

unstable
Since v4.0.0

Encodes a key in a format.

Details

The format must hold the key's role and be writable: spki for public keys, pkcs8 for private keys, jwk for every role, and the PEM forms of SPKI and PKCS#8. PKCS#1 and SEC1 cannot be written.

PEM output uses 64-column lines. JWK output contains only kty, crv, and the key material. A non-extractable CryptoKey-backed key fails with a NotExtractable reason.

Signature

declare function encode<K extends Key, Data>(format: Format<RoleOf<K>, "encode", Data>, key: K): Effect<Data, CryptoError>

Example Writing a public key as JWK

import { Effect, Layer } from "effect"
import { KeyFormat, SecureRandom, Signature } from "effect/crypto"
const program = Effect.gen(function*() {
const signature = yield* Signature.Signature
const { publicKey } = yield* signature.generateKeyPair(Signature.ed25519)
const jwk = yield* KeyFormat.encode(KeyFormat.jwk, publicKey)
return [jwk.kty, jwk.crv]
})
const layer = Signature.layerWebCrypto.pipe(Layer.provide(SecureRandom.layerWebCrypto))
await Effect.runPromise(program.pipe(Effect.provide(layer))) // => ["OKP", "Ed25519"]

Formats

jwk

unstable
Since v4.0.0

JSON Web Key (RFC 7517), for every role.

Details

decodePublic rejects a JWK with d, and decodePrivate requires it. kty, crv, and alg must match the algorithm when present. Written JWKs contain only kty, crv, and the key material.

Ed25519 and X25519 private JWKs need a correct x: Bun and Deno accept d alone, but other runtimes reject it.

Signature

declare const jwk: Format<Role, Direction, JsonWebKey>

pem

unstable
Since v4.0.0

PEM (RFC 7468) over a DER format.

Details

Reading accepts exactly one block whose label matches the format: PUBLIC KEY, PRIVATE KEY, RSA PRIVATE KEY, or EC PRIVATE KEY. Writing uses 64-column lines.

Signature

declare function pem<R extends Role, D extends Direction>(format: Format<R, D, Uint8Array<ArrayBufferLike>>): Format<R, D, string>

pkcs1

unstable
Since v4.0.0

PKCS#1 RSAPrivateKey DER (RFC 8017), read only.

Signature

declare const pkcs1: Format<"private", "decode", Uint8Array>

pkcs8

unstable
Since v4.0.0

PKCS#8 PrivateKeyInfo DER (RFC 5208), for private keys.

Signature

declare const pkcs8: Format<"private", Direction, Uint8Array>

sec1

unstable
Since v4.0.0

SEC1 ECPrivateKey DER (RFC 5915), read only.

Details

The curve parameters, when present, must match the algorithm's curve. The public point must be present.

Signature

declare const sec1: Format<"private", "decode", Uint8Array>

spki

unstable
Since v4.0.0

SubjectPublicKeyInfo DER (RFC 5280), for public keys.

Signature

declare const spki: Format<"public", Direction, Uint8Array>

Models

AsymmetricAlgorithm

type unstable
Since v4.0.0

The algorithms whose public and private keys KeyFormat reads and writes.

Signature

type AsymmetricAlgorithm = Signature.Algorithm | KeyAgreement.Algorithm | KeyWrap.RsaOaep

Direction

type unstable
Since v4.0.0

Whether a format can be read, written, or both.

Signature

type Direction = "decode" | "encode"

Format

interface unstable
Since v4.0.0

A key format, such as spki or pem(pkcs8).

Details

~tags exists only in the types. It records the roles the format holds, whether it can be read and written, and the type of its data: Uint8Array for DER, string for PEM, and JsonWebKey for JWK.

Signature

interface Format<R extends Role, D extends Direction, Data> {
readonly _tag: "spki" | "pkcs8" | "jwk" | "pkcs1" | "sec1";
readonly "~tags"?: {
readonly data: Data;
readonly direction: (_: D) => void;
readonly role: (_: R) => void;
};
readonly pem: boolean;
}

Key

type unstable
Since v4.0.0

Any key KeyFormat can write.

Signature

type Key = PublicKey | PrivateKey | SecretKey

PrivateKey

type unstable
Since v4.0.0

The private key type of an algorithm.

Signature

type PrivateKey<A extends AsymmetricAlgorithm = AsymmetricAlgorithm> = A extends Signature.Algorithm ? Signature.PrivateKey<A> : A extends KeyAgreement.Algorithm ? KeyAgreement.PrivateKey<A> : A extends KeyWrap.RsaOaep ? KeyWrap.PrivateKey<A> : never

PublicKey

type unstable
Since v4.0.0

The public key type of an algorithm.

Signature

type PublicKey<A extends AsymmetricAlgorithm = AsymmetricAlgorithm> = A extends Signature.Algorithm ? Signature.PublicKey<A> : A extends KeyAgreement.Algorithm ? KeyAgreement.PublicKey<A> : A extends KeyWrap.RsaOaep ? KeyWrap.PublicKey<A> : never

Role

type unstable
Since v4.0.0

The role of a key: public, private, or secret (symmetric).

Signature

type Role = "public" | "private" | "secret"

RoleOf

type unstable
Since v4.0.0

The role of a key type.

Signature

type RoleOf<K extends Key> = K extends PublicKey ? "public" : K extends PrivateKey ? "private" : "secret"

SecretAlgorithm

type unstable
Since v4.0.0

The algorithms whose secret keys KeyFormat reads and writes, and that KeyWrap wraps.

Signature

type SecretAlgorithm = Aead.Algorithm | Cipher.Algorithm | Mac.Algorithm | KeyWrap.AesKw

SecretKey

type unstable
Since v4.0.0

The secret key type of an algorithm.

Signature

type SecretKey<A extends SecretAlgorithm = SecretAlgorithm> = A extends Aead.Algorithm ? Aead.Key<A> : A extends Cipher.Algorithm ? Cipher.Key<A> : A extends Mac.Algorithm ? Mac.Key<A> : A extends KeyWrap.AesKw ? KeyWrap.Key<A> : never