DKIM (DomainKeys Identified Mail)
How cryptographic signatures prove an email hasn't been tampered with
In 2012, a vulnerability researcher demonstrated that he could send emails as any @gmail.com address and have them land in inboxes without a single warning. The trick was straightforward: Google’s DKIM signing key for Gmail was only 512 bits long. He factored it in about 72 hours using commodity hardware. With the private key in hand, he could sign messages that passed DKIM verification perfectly — Google’s own cryptographic seal, applied to forged messages.
Google rotated the key within days and upgraded to 2048-bit RSA. But the incident exposed a fundamental truth about DKIM: the system is only as strong as the keys behind it, and those keys live in plain sight, published in DNS for the world to read.
DKIM is the cryptographic layer of email authentication. Where SPF answers “did this message come from an authorized server?”, DKIM answers a different and equally important question: “has this message been tampered with since it was sent, and can the sending domain prove it?”
The Problem: Messages Can Be Altered in Transit
Email travels through multiple servers on its way from sender to recipient. Any of those intermediate servers — or any attacker with network access — could theoretically modify the message contents. Change an invoice amount, replace a link, alter instructions. SPF cannot detect this. SPF only validates that the sending server’s IP address was authorized; it says nothing about what happened to the message after it left.
DKIM solves this by attaching a cryptographic signature to every outgoing message. The signature covers specific headers and a hash of the message body. If anything is altered after signing, the signature will not verify. The recipient can confirm, cryptographically, that the message arrived exactly as the sender intended.
How DKIM Works
DKIM uses asymmetric cryptography — a private/public key pair. The process has two sides:
Signing (Sender Side)
- The sending mail server generates a hash of selected email headers (
From,To,Subject,Date, etc.) and the message body - It encrypts that hash using the domain’s private key (kept secret on the mail server)
- The resulting signature is added to the email as a
DKIM-Signatureheader
Verification (Recipient Side)
- The receiving mail server reads the
DKIM-Signatureheader - It extracts two critical values:
d=(the signing domain) ands=(the selector) - It performs a DNS TXT lookup for
<selector>._domainkey.<domain>— for example,google._domainkey.example.com - It retrieves the public key from the DNS record
- It uses the public key to decrypt the signature, revealing the original hash
- It independently computes the hash of the received message’s headers and body
- If the two hashes match, the DKIM check passes — the message has not been tampered with and was signed by the claimed domain
The elegance of this system is that the public key lives in DNS, which is a globally distributed, cached, and highly available infrastructure. No special key exchange protocol is needed. Any receiving server in the world can verify a signature by performing a single DNS lookup.
Anatomy of a DKIM-Signature Header
Here is a real-world DKIM-Signature header, broken across lines for readability:
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
d=example.com; s=selector1;
h=from:to:subject:date:message-id;
bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
b=AuUoFEfDxTDkHlLXSZEpZj79LICEps6eda7W3deTVFOk2P... Each tag serves a specific purpose:
| Tag | Meaning | Example Value |
|---|---|---|
v | Version (always 1) | 1 |
a | Signing algorithm | rsa-sha256 |
c | Canonicalization (how headers/body are normalized before hashing) | relaxed/relaxed |
d | Signing domain | example.com |
s | Selector (identifies which key pair was used) | selector1 |
h | List of headers included in the signature | from:to:subject:date:message-id |
bh | Body hash (base64-encoded hash of the message body) | 2jUSOH9N... |
b | The signature itself (base64-encoded) | AuUoFEfD... |
The canonicalization field (c=) deserves a note. Email messages frequently get subtly reformatted as they pass through servers — trailing whitespace added, header fields re-wrapped. The relaxed mode tolerates these cosmetic changes (ignoring extra whitespace, lowercasing header names). The simple mode requires exact byte-for-byte matches. Most deployments use relaxed/relaxed to avoid false failures from benign reformatting.
The DKIM DNS Record
The public key is published as a TXT record at a specific location: <selector>._domainkey.<domain>.
selector1._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC..." Record Tags
| Tag | Required | Description | Example |
|---|---|---|---|
v | Recommended | Version (always DKIM1) | v=DKIM1 |
k | Optional | Key type (default: rsa) | k=rsa or k=ed25519 |
p | Required | Public key, base64-encoded | p=MIGfMA0... |
t | Optional | Flags | t=s (strict alignment) or t=y (testing mode) |
h | Optional | Acceptable hash algorithms | h=sha256 |
s | Optional | Service type | s=email or s=* (all services) |
n | Optional | Notes (human-readable) | n=Production signing key |
Selectors: Why They Exist
The selector mechanism allows a single domain to have multiple active DKIM keys simultaneously. This is essential because:
- Different email services sign with different keys (your corporate mail uses one key, your marketing platform uses another)
- Key rotation requires a transition period where both old and new keys are valid
- Different departments or systems may manage their own signing infrastructure
Each email service has its own conventional selector names:
| Provider | Typical Selector(s) | DNS Record Location |
|---|---|---|
| Google Workspace | google | google._domainkey.example.com |
| Microsoft 365 | selector1, selector2 | selector1._domainkey.example.com |
| Amazon SES | Custom (e.g., 224i4yxa5dv7c2xz...) | Via CNAME to amazonses.com |
| Mailchimp | k1 | k1._domainkey.example.com |
| SendGrid | s1, s2 | s1._domainkey.example.com |
Microsoft uses two selectors (selector1 and selector2) to facilitate automatic key rotation — one is active while the other is being prepared for the next rotation.
Key Sizes: How Strong Is Strong Enough?
The public key in DNS is visible to everyone. An attacker who can factor it (derive the private key from the public key) can forge DKIM signatures for that domain. Key size determines how difficult this is.
| Key Size | Status | Notes |
|---|---|---|
| 512-bit RSA | Broken | Can be factored in hours on consumer hardware; never use |
| 1024-bit RSA | Deprecated | Can be broken in under 4 days using cloud computing; Google and Yahoo recommend against it |
| 2048-bit RSA | Current standard | Recommended by NIST, Google, Yahoo, and Microsoft as the minimum |
| 4096-bit RSA | Maximum practical | Rarely used; the base64-encoded key can exceed DNS TXT record size limits |
| Ed25519 | Emerging | Much smaller keys, faster computation; defined in RFC 8463 (2018); limited receiver support |
The DNS constraint is worth understanding. A single DNS TXT record string can hold 255 bytes. A 2048-bit RSA key encodes to roughly 392 base64 characters, which fits. A 4096-bit key doubles that, requiring the TXT record to be split across multiple strings within a single record — which is valid per the DNS specification but can cause issues with some DNS providers and resolvers.
Ed25519 is the future-looking option: an Ed25519 public key is only 44 base64 characters, trivially fitting in DNS. It is faster to compute and cryptographically strong with much smaller keys. However, as of 2025, receiver support remains limited, so most deployments stick with 2048-bit RSA.
Key Rotation
DKIM keys should be rotated periodically. If a private key is compromised, all messages signed with it can be forged until the key is revoked.
Recommended rotation frequency:
| Key Size | Rotation Interval |
|---|---|
| 1024-bit | Every 3 months (upgrade to 2048-bit immediately) |
| 2048-bit | Every 6-12 months |
| 4096-bit | Every 12 months |
The rotation procedure:
- Generate a new key pair with a new selector name (e.g.,
selector202501) - Publish the new public key in DNS under the new selector
- Wait for DNS propagation — 24 to 48 hours to be safe
- Configure the sending server to sign outgoing mail with the new private key and new selector
- Keep the old public key in DNS for a transition period (messages signed with the old key may still be in transit or in recipients’ inboxes awaiting verification)
- After the transition, revoke the old key by publishing an empty
p=tag:
old-selector._domainkey.example.com. IN TXT "v=DKIM1; p=" An empty p= tag is the standard signal that a key has been revoked. Any message bearing a signature with this selector will fail DKIM verification.
What DKIM Does and Does Not Protect
DKIM proves:
- The message was signed by the domain in the
d=tag - The signed headers and body have not been altered since signing
- The signing domain takes responsibility for the message
DKIM does not prove:
- That the
From:header matches the signing domain (that is DMARC’s job through alignment) - That the message was not sent by a compromised account (the signature is valid regardless of who triggered the send)
- That unsigned headers were not added or modified (only headers listed in the
h=tag are covered) - That the message is not spam or malicious (DKIM is an identity assertion, not a content filter)
A critical nuance: DKIM survives forwarding. Unlike SPF, which breaks when a message is forwarded (the forwarding server’s IP is not in the original SPF record), the DKIM signature travels with the message. As long as the signed headers and body are not modified, verification still passes. This is why DMARC can fall back to DKIM alignment when SPF fails due to forwarding.
How to Verify DKIM with dig
To look up a DKIM public key, you need to know the selector. You can find the selector by examining the DKIM-Signature header of a received email (look for the s= tag). Then query DNS:
dig +short TXT google._domainkey.example.com If the domain uses Google Workspace, this might return:
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..." For Microsoft 365:
dig +short TXT selector1._domainkey.example.com If the record is a CNAME (common with hosted email services), follow the chain:
dig +short CNAME selector1._domainkey.example.com selector1-example-com._domainkey.example.onmicrosoft.com. Then resolve the final TXT:
dig +short TXT selector1-example-com._domainkey.example.onmicrosoft.com To check if a key has been revoked, look for an empty p= tag in the response.
Adoption
DKIM adoption has historically lagged behind SPF because it requires more than a DNS record. It demands cryptographic key generation, mail server configuration to sign outgoing messages, and ongoing key rotation — operational overhead that SPF does not impose.
| Metric | Value | Source |
|---|---|---|
| Alexa Top 1M domains with DKIM | ~28.1% | USENIX Security (2022) |
| French .fr domains with DKIM | 40.7% | AFNIC (2025) |
| DKIM records that fail validation | 3.4% | dmarcchecker.app (2024) |
| Misconfigured DKIM records | 2.9% of adopters | USENIX Security (2022) |
The growth trajectory is encouraging. French .fr domains saw DKIM adoption nearly double from 22.6% in 2023 to 40.7% in 2025, driven largely by Google and Yahoo’s February 2024 enforcement requirements that mandate both SPF and DKIM for bulk senders.
DKIM was born from the merger of two competing proposals: Yahoo’s DomainKeys (2004) and Cisco’s Identified Internet Mail (2005). The combined specification was published as RFC 4871 in 2007, updated to the current standard RFC 6376 in September 2011, and extended with Ed25519 key support in RFC 8463 in September 2018.
Together with SPF and DMARC, DKIM forms the email authentication triad. SPF validates the sending server. DKIM validates the message integrity. And DMARC ties them both to the domain visible in the From: header, adding the policy layer that tells receivers what to do when authentication fails.