# Keys, Signing and Encryption

> The per-agent key behind a Whisper identity: the DANE TLSA pin, the identity CA, SSHFP, SMIMEA, OPENPGPKEY, did:web, whisper sign and whisper encrypt.

*Source: https://www.whisper.security/docs/identity/keys*

---
Each identity holds one elliptic-curve key on the P-256 curve. Everything on this page is that key, published a different way for a different stack. The SHA-256 digest of its subject public key info is the single value that ties them together, so a recipient who learns the digest from DNSSEC-signed DNS can check any of these surfaces without asking Whisper anything.

## The DANE pin {#tlsa}

`TLSA` at `_443._tcp.<name>`, parameters `3 1 1`: DANE-EE usage, SPKI selector, SHA-256 match. This is the authoritative binding between the name and the key.

```bash
dig +dnssec TLSA _443._tcp.ae3b051ff3bf7f478.t<tenant>.agents.whisper.online
```

To check a live connection against it in one command:

```bash
openssl s_client -dane_tlsa_domain ae3b051ff3bf7f478.t<tenant>.agents.whisper.online \
  -dane_tlsa_rrdata "3 1 1 <hex digest>" \
  -connect ae3b051ff3bf7f478.t<tenant>.agents.whisper.online:443 </dev/null
```

## The key in DNS {#agentkey}

`TXT` at `_whisper-agentkey.<name>` publishes the same key as base64 SPKI, tagged with its algorithm, for tooling that reads `TXT` and not `TLSA`.

```bash
dig +short TXT _whisper-agentkey.ae3b051ff3bf7f478.t<tenant>.agents.whisper.online
# "v=whisper1; k=p256; p=<base64 SPKI>"
```

## The identity certificate authority {#ca}

Each identity gets its own leaf certificate, and the blast radius of that certificate is exactly one identity. The leaf is anchored by the `TLSA` pin, not by a browser root, so a plain `curl` against an identity's name fails until you supply the root or check the pin yourself. Rotation is make-before-break: the new pin is published before the new leaf is served.

```bash
curl -sO https://rdap.whisper.online/.well-known/whisper-ca.pem
curl -s https://rdap.whisper.online/.well-known/whisper-ca.json   # fingerprints
```

For a runtime that reads a CA bundle rather than a pin, point it at the root: set `SSL_CERT_FILE`, or `NODE_EXTRA_CA_CERTS` on Node. The full chain is at `/.well-known/whisper-ca-chain.pem`.

## The JSON Web Key Set {#jwks}

`GET /.well-known/jwks.json` on the identity's own name serves the same key in JWK form, for JOSE libraries. It is the key that signs the identity document described in [The Proof Chain](/docs/identity/proofs#identity-document).

## The DID document {#did-web}

`GET /.well-known/did.json` on the identity's own name serves a `did:web` document for `did:web:<name>`. It carries two P-256 keys: an `authentication` key, which signs the identity document, and an `assertionMethod` key, which is the same key the `TLSA` record pins. Each key's fragment is the SHA-256 digest of the key itself.

```bash
curl -s --cacert whisper-ca.pem \
  https://ae3b051ff3bf7f478.t<tenant>.agents.whisper.online/.well-known/did.json | jq .
```

## SSH host keys {#sshfp}

`SSHFP` is published at the identity's own name, algorithm 3 with fingerprint type 2, so an SSH client can authenticate the host from DNSSEC instead of a prompt.

```bash
dig +dnssec SSHFP ae3b051ff3bf7f478.t<tenant>.agents.whisper.online
ssh -o VerifyHostKeyDNS=yes ae3b051ff3bf7f478.t<tenant>.agents.whisper.online
```

## The S/MIME pin {#smimea}

`SMIMEA` publishes the same key under `<hash>._smimecert.<name>` with parameters `3 1 1`, for the mailbox `agent@<name>`. The label hash is fixed, so it is derivable from the name alone.

```bash
dig +short SMIMEA <hash>._smimecert.ae3b051ff3bf7f478.t<tenant>.agents.whisper.online
```

## The OpenPGP key {#openpgpkey}

`OPENPGPKEY` publishes the key under `_openpgpkey.<name>` as a version-4 packet on the NIST P-256 curve, which is where `whisper encrypt` finds a recipient's key.

## Signing a file {#sign}

`whisper sign` produces a detached CMS signature beside the file. A recipient checks it against the `SMIMEA` pin, so there is no certificate to exchange first.

```bash
whisper sign file report.md                       # writes report.md.p7s
whisper sign verify report.md --sig report.md.p7s
```

`whisper verify --signature` checks a compact ES256 JWS signed by the same key, which is the shape to use when the artifact is a token rather than a file.

## Encrypting to an identity {#encrypt}

`whisper encrypt` looks the recipient's key up over DANE and seals the file to it. Nothing is exchanged in advance, and the sender needs no account.

```bash
whisper encrypt --to ae3b051ff3bf7f478.t<tenant>.agents.whisper.online -o secret.wenc plan.txt
whisper decrypt secret.wenc
```

The container is HPKE: DHKEM on P-256 with HKDF-SHA256, HKDF-SHA256 for key schedule, AES-256-GCM for the payload. The armored form carries the header `WHISPER ENCRYPTED MESSAGE`.

## What the pin proves {#scope}

The pin proves that one name and one key were published together by the operator of the zone, and that the party answering on that name holds the key. Two limits are worth stating plainly.

A sealed file proves that the recipient's key was used. It does not authenticate the sender, so pair encryption with a signature when you need to know who sent something.

Where Whisper holds the key on the identity's behalf, a signature proves the key was used and not who used it. An identity that supplies its own public key and keeps the private half on its own hardware closes that gap; [Vehicle Identities](/docs/identity/vehicles) is the shape that does this by default.
