# DKIM (DomainKeys Identified Mail)

> RFC 6376 reference plus RFC 8301 (rsa-sha256 required, 1024–4096-bit keys) and RFC 8463 (ed25519-sha256) — signature and key record syntax, tag meanings, canonicalization, signing scope, and risks.

Source: emailmarketing.net — https://emailmarketing.net/learn/authentication/dkim

DKIM lets your domain take responsibility for a message by signing it. Receivers check the signature against a public key you publish in DNS. A valid signature proves that the signed content was not changed after signing, and it ties the message to the **d=** domain, which is the identifier DMARC checks for alignment (see [DMARC](https://emailmarketing.net/learn/authentication/dmarc)). Unlike [SPF](https://emailmarketing.net/learn/authentication/spf), DKIM survives forwarding, as long as the signed content is not modified.

DKIM is defined in RFC 6376. It authenticates the content of the message: the signer adds a `DKIM-Signature` header that contains cryptographic hashes of selected headers and of the body.

## The DKIM-Signature header: tag reference

| Tag | Req? | Meaning |
|---|---|---|
| `v=` | Required | Version; must be `1`. |
| `a=` | Required | Signing algorithm: `rsa-sha256` or `ed25519-sha256` (RFC 8463). `rsa-sha1` **MUST NOT** be used (RFC 8301). |
| `b=` | Required | The signature itself, in base64. Whitespace is ignored. |
| `bh=` | Required | The hash of the canonicalized body (limited by `l=` if present), in base64. |
| `c=` | Optional | Canonicalization, written as `header/body`, where each part is `simple` or `relaxed`. The default is `simple/simple`, and `c=relaxed` on its own means `relaxed/simple`. |
| `d=` | Required | **SDID**, the signing domain that claims responsibility. It must have a key record in DNS. This is the identifier used for DMARC alignment. |
| `h=` | Required | An ordered list of the signed header fields, separated by colons. It must not be empty and **must not** include the DKIM-Signature being created. It may list the same name several times, and may list fields that do not exist (see oversigning below). |
| `i=` | Optional | **AUID**, the identity of the agent or user: an address whose domain must be the same as `d=` or a subdomain of it (exactly the same if the key record has the flag `t=s`). The default is `@` followed by the d= value. |
| `l=` | Optional | Body length: the number of canonicalized body octets covered by `bh=`. The default is the entire body. **This is a security risk; see below.** |
| `q=` | Optional | The method for retrieving the key. Only `dns/txt` is defined (the default). |
| `s=` | Required | The selector, which names the key within the domain (see the key record below). |
| `t=` | Recommended | The signature timestamp, in Unix seconds. Verifiers may ignore signatures with timestamps in the future. |
| `x=` | Recommended | An absolute expiration timestamp, which must be > `t=`. It is not a defense against replay. |
| `z=` | Optional | A copy of selected header fields at signing time, separated by `|`, for diagnosis. |

### Signing scope: which headers to sign

- The **From:** field **MUST** be signed. It is the identity DKIM exists to protect, and the field DMARC evaluates. In practice, also sign Subject, Date, To, Reply-To, Message-ID, the MIME headers and the List-* fields.
- **Oversigning:** listing a header name in `h=` one more time than the header appears signs a "null" instance of it. Any header of that name added later (e.g., a second From: or Subject: inserted along the way) then breaks the signature. This is recommended for From, Subject, To and Reply-To.
- When a header appears several times, its instances are signed **from the bottom up**. You cannot choose to sign only one of them.

### The risk of the `l=` body length tag

`l=` lets a signature cover only the first N octets of the body, so that intermediaries can add content (e.g., mailing list footers) without breaking the signature. As a result, **anything added after N octets is not verified**. An attacker can replay a signed message with malicious content added at the end, and the signature still validates. This was exploited in practice ("DKIM l= vulnerability", 2024). **Do not sign with `l=`. Verifiers should treat signatures with `l=` with suspicion, or ignore the exemption the tag grants.**

## The DNS key record

The key is published as a TXT record at:

```
<selector>._domainkey.<domain>
```

| Tag | Default | Meaning |
|---|---|---|
| `v=` | `DKIM1` | Version. If present, it must be the first tag. |
| `p=` | — (required) | The public key, in base64. **An empty `p=` means the key is revoked.** |
| `k=` | `rsa` | Key type: `rsa` or `ed25519` (RFC 8463). |
| `h=` | all allowed | The acceptable hash algorithms, separated by colons (`sha256`; `sha1` is obsolete). |
| `s=` | `*` | Service type: `*` or `email`. |
| `t=` | none | Flags: `y` means testing mode; `s` means the `i=` domain must be exactly the same as `d=` (no AUIDs on subdomains). |
| `n=` | empty | Notes for administrators to read. |

Example (Ed25519, from RFC 8463):

```
brisbane._domainkey.football.example.com. IN TXT
  "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="
```

**Selector hygiene:** selectors let you run several keys at the same time (for each system, each ESP or each date). To rotate keys, publish a new selector, switch signing to it, then revoke the old key. Never reuse a selector with a new key, because you lose the ability to tell old mail that was legitimately signed from forgeries.

## Algorithms and key sizes (RFC 8301, RFC 8463)

| Rule | Requirement |
|---|---|
| Signing algorithm | Signers **MUST** sign with `rsa-sha256`. `rsa-sha1` **MUST NOT** be used for signing or verifying (SHA-1 is historic). |
| RSA key size for signers | **MUST** use keys of ≥ 1024 bits. **2048 bits is recommended** (the current operational baseline). |
| RSA key size for verifiers | **MUST** validate keys **from 1024 to 4096 bits**, and MAY support larger keys. **MUST NOT** treat signatures made with keys of < 1024 bits as valid. |
| Ed25519 (RFC 8463) | `a=ed25519-sha256`, key record `k=ed25519`, and `p=` is the Ed25519 public key in base64. The keys are 256 bits, so the encoded key is only 44 octets and fits in one TXT string of 255 bytes (large RSA keys often have to be split across strings). |
| Dual signing | For compatibility, sign with **both** `rsa-sha256` and `ed25519-sha256`, using **different selectors** (one key record for each selector). Support for Ed25519 among verifiers is still not universal. |

## Canonicalization

Canonicalization normalizes the content before hashing, so that the signature tolerates rewriting in transit. It does not change the message that is sent.

| Algorithm | Rules |
|---|---|
| `simple` (header) | Headers are used exactly as they appear, so any change breaks the signature. |
| `relaxed` (header) | Header names are lowercased, continuation lines are unfolded, runs of whitespace are reduced to a single space, trailing whitespace is removed, and whitespace around the colon is removed. |
| `simple` (body) | Trailing empty lines are removed, and the body ends with a single CRLF. |
| `relaxed` (body) | Whitespace at the end of lines is removed, runs of whitespace within lines are reduced to a single space, trailing empty lines are removed, and the body ends with a CRLF. |

**`relaxed/relaxed` is the default in practice.** `simple` header canonicalization breaks whenever any MTA folds or wraps headers again.

## Verification behavior

- Each signature has one of three outcomes: **SUCCESS**, **PERMFAIL** (the failure cannot be recovered from: a bad signature, a revoked or undersized key, a syntax error) or **TEMPFAIL** (the failure is temporary, e.g., a DNS timeout).
- A failed signature is treated **as if the message were unsigned**. A DKIM failure is not in itself a reason to reject the message. It simply provides no positive identifier. DMARC is what turns the absence of an aligned pass into a policy decision.
- **Multiple signatures** are each evaluated on their own. Verifiers keep checking until one verifies. A message can legitimately carry a signature from the author's domain and signatures from one or more intermediaries.
- The verifier's output must include the `d=` domain and the result. It appears later in the [Authentication-Results header](https://emailmarketing.net/learn/authentication/authentication-results-header).

## Deliverability notes

- Mailbox providers increasingly tie reputation to the **DKIM d= domain**, and prefer DKIM to SPF for DMARC alignment, enrollment in feedback loops and tools for senders.
- The requirements for bulk senders at Gmail and Yahoo in effect make an aligned DKIM signature, with a key of ≥1024 bits (2048 recommended), mandatory at scale.
- DKIM does not prevent **replay**. A spammer can resend a legitimately signed message unchanged to new recipients, and the signature still passes, which uses up the reputation of the d= domain. Mitigations are short `x=` expiration times, selectors for each recipient or stream, and monitoring.

## Related articles

- [SPF](https://emailmarketing.net/learn/authentication/spf), which authenticates the sending path instead
- [DMARC](https://emailmarketing.net/learn/authentication/dmarc), on alignment and policy built on DKIM and SPF
- [ARC](https://emailmarketing.net/learn/authentication/arc), on the chain of custody when intermediaries break signatures
