# `SoftSigner.PKCS8`
[🔗](https://github.com/utaladriz/pkcs11ex/blob/v0.1.0/lib/soft_signer/pkcs8.ex#L1)

Software signer backed by a PKCS#8 PEM private key plus a
separate certificate (PEM, single or chained).

Different shape from `SoftSigner.PKCS12`: PKCS#12 bundles carry
the cert with the key, so cert-chain extraction is automatic.
PKCS#8 is just the key, so the caller supplies the cert
separately. This split is common in cloud deployments where
TLS-style key + cert files live next to each other on disk.

Supports both encrypted (`-----BEGIN ENCRYPTED PRIVATE KEY-----`)
and unencrypted PKCS#8, plus the older PKCS#1
`-----BEGIN RSA PRIVATE KEY-----` format.

## Threat model — key material is BEAM-resident

Same threat model as `SoftSigner.PKCS12` — the decoded RSA private
key lives in BEAM heap memory for the lifetime of any reference to
the struct. The BEAM cannot zeroize managed terms; the GC may
retain freed copies. The key is readable by anyone with BEAM-process
memory access (debuggers, `/proc/<pid>/mem`, core dumps, swapped
pages, hibernation snapshots).

Use `pkcs11ex` instead for threat models that require the key to
stay on hardware. The package boundary between `:soft_signer` and
`:pkcs11ex` is intentional: omitting `:soft_signer` from
`mix.lock` prevents software signing by deployment topology, not
just by configuration.

## Usage

    # From file paths:
    {:ok, signer} =
      SoftSigner.PKCS8.load(
        key_path: "/keys/legal-proxy.key.pem",
        cert_path: "/keys/legal-proxy.cert.pem",
        password: "secret"     # only needed if the key is encrypted
      )

    # From in-memory PEM strings (the same opts but suffix `_pem`):
    {:ok, signer} =
      SoftSigner.PKCS8.load(
        key_pem: File.read!("/keys/legal-proxy.key.pem"),
        cert_pem: File.read!("/keys/legal-proxy.cert.pem")
      )

    {:ok, signed_pdf} =
      SignCore.PDF.sign(pdf,
        signer: signer,
        alg: :PS256,
        x5c: SoftSigner.PKCS8.cert_chain(signer)
      )

## Options

Required (key — pick one):
  * `:key_path` — filesystem path to a PEM file.
  * `:key_pem` — PEM bytes already in memory.

Required (cert — pick one):
  * `:cert_path` — filesystem path to a PEM file. May contain
    a single cert or a chain (leaf first, then intermediates).
  * `:cert_pem` — PEM bytes already in memory.

Optional:
  * `:password` — required only if the key PEM is encrypted.
    Surfaces `{:error, :wrong_password}` on a mismatch.

# `t`

```elixir
@type t() :: %SoftSigner.PKCS8{
  chain_ders: [binary()],
  leaf_der: binary(),
  rsa_key: tuple()
}
```

# `cert_chain`

```elixir
@spec cert_chain(t()) :: [binary()]
```

Returns the cert chain as `[leaf_der | intermediates_der]` —
drop into any format adapter's `:x5c` opt.

# `load`

```elixir
@spec load(keyword()) :: {:ok, t()} | {:error, term()}
```

Load a PKCS#8 key + cert chain. See moduledoc for opts.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
