Module 5 · Lesson 4

DKIM: DomainKeys Identified Mail

15 min read

dkimemail-authenticationcryptographydnspostfixkey-rotation

DKIM: DomainKeys Identified Mail

A message from your domain can survive SPF failing and still be authenticated by the receiving server. That's DKIM. It's a cryptographic signature applied at the point of sending, attached to the email headers, and verifiable by anyone who looks up your public key in DNS. Forwarding doesn't break it. Third-party delivery doesn't break it — as long as the signing key is yours.

How DKIM Works

Your mail server generates an RSA (or Ed25519) key pair. The private key stays on the server. The public key gets published in DNS as a TXT record.

When your server sends a message, it:

  1. Takes a hash of specific email headers and the body.
  2. Signs the hash with the private key.
  3. Adds a DKIM-Signature header to the message.

When the receiving server gets the message:

  1. It reads the DKIM-Signature header to find the domain (d=) and selector (s=).
  2. It looks up <selector>._domainkey.<domain> in DNS to get the public key.
  3. It verifies the signature against the headers and body.
  4. Pass or fail.

The DKIM-Signature Header

Here's what a DKIM-Signature header actually looks like:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
    d=example.com; s=mail2024;
    h=from:to:subject:date:message-id;
    bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=;
    b=abc123...longbase64string...xyz789

Key fields:

  • d= — the signing domain
  • s= — the selector (tells receivers which public key to look up)
  • h= — which headers were signed
  • bh= — hash of the body
  • b= — the actual signature

The selector s=mail2024 means the public key is at mail2024._domainkey.example.com.

The DNS Record

mail2024._domainkey.example.com.   300   IN   TXT   "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC..."
  • v=DKIM1 — required version string
  • k=rsa — key type (rsa or ed25519)
  • p= — the base64-encoded public key

Generating a Key Pair

For a self-managed mail server (Postfix, Exim, etc.):

# Generate a 2048-bit RSA key pair
openssl genrsa -out /etc/dkim/private/mail2024.private 2048
openssl rsa -in /etc/dkim/private/mail2024.private -pubout -out /etc/dkim/public/mail2024.public

# Extract the public key value for the DNS record
openssl rsa -in /etc/dkim/private/mail2024.private -pubout -outform DER | \
  openssl enc -base64 | tr -d '\n'

That base64 string goes into the p= field of your TXT record.

Why 2048 and not 4096?

4096-bit RSA keys generate public key strings that exceed 255 bytes — the length limit for a single DNS TXT string. DNS TXT records can technically span multiple strings (each up to 255 bytes, concatenated), but many DNS providers and resolvers have issues with this in practice. The bigger problem: 4096-bit public keys often push the total DNS response over 512 bytes, causing the resolver to fall back to TCP instead of UDP. Many resolvers handle this correctly; some don't. The result is intermittent DKIM verification failures that are extremely difficult to debug.

Stick with 2048-bit. It's secure enough (no practical attacks exist) and works reliably across all DNS implementations. Ed25519 keys are shorter and stronger — if your mail software supports them, prefer Ed25519 over RSA.

Setting Up DKIM in Postfix (with OpenDKIM)

# Install
apt install opendkim opendkim-tools

# Generate the key for selector "mail2024"
opendkim-genkey -s mail2024 -d example.com -D /etc/dkim/keys/

# This creates:
# /etc/dkim/keys/mail2024.private  — keep this secret
# /etc/dkim/keys/mail2024.txt      — put this in DNS

cat /etc/dkim/keys/mail2024.txt
# mail2024._domainkey   IN   TXT   ( "v=DKIM1; k=rsa; "
#           "p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC..." )

Configure /etc/opendkim.conf:

Domain                  example.com
KeyFile                 /etc/dkim/keys/mail2024.private
Selector                mail2024
Socket                  inet:8891@localhost

Configure Postfix to use OpenDKIM in /etc/postfix/main.cf:

milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Restart both services, send a test email, check the headers.

Verifying DKIM

# Check the DNS record
dig TXT mail2024._domainkey.example.com +short

# If your DNS TXT record is too long, dig may truncate it
# Use this to see the full record:
dig TXT mail2024._domainkey.example.com

# Send a test email to check-auth@verifier.port25.com
# They reply with a full authentication report

A passing DKIM header in received mail:

Authentication-Results: mx.google.com;
    dkim=pass header.i=@example.com header.s=mail2024

A failing check:

Authentication-Results: mx.google.com;
    dkim=fail (bad signature) header.i=@example.com

Key Rotation

DKIM keys should be rotated periodically. The catch: you can't just swap the key. The new key needs to be published in DNS before you start signing with it. And the old key needs to stay in DNS for a while after you stop using it — for messages in transit that were signed before the cutover.

The rotation procedure:

  1. Generate a new key pair with a new selector (e.g., mail2025).
  2. Publish mail2025._domainkey.example.com in DNS.
  3. Wait for TTL to expire so the record propagates.
  4. Switch your mail server to sign with mail2025.
  5. Wait for messages signed with mail2024 to clear transit (24-48 hours is usually enough).
  6. Remove the mail2024 DNS record.
  7. Optionally, keep the old record with p= set to empty string to signal revocation:
    mail2024._domainkey.example.com.   IN   TXT   "v=DKIM1; p="
    
    An empty p= means the key is revoked.

Selector naming convention: Use a date-based convention (mail2024, mail2025, or 20240301) so you always know when a key was put into service.

Key Takeaways

  • DKIM signs email headers and body with your private key. Receivers verify with your public key in DNS.
  • The public key lives at <selector>._domainkey.<domain> as a TXT record.
  • Use 2048-bit RSA or Ed25519. Avoid 4096-bit RSA — the public key may exceed DNS response size limits.
  • Rotation requires two selectors in parallel during the transition window.
  • An empty p= field revokes a DKIM key.
  • DKIM survives email forwarding, which SPF does not.

Further Reading

  • RFC 6376 — DomainKeys Identified Mail (DKIM) Signatures
  • RFC 8463 — A New Cryptographic Signature Method for DKIM (Ed25519)
  • mxtoolbox.com/dkim.aspx — look up any DKIM selector
  • port25.com/support/authentication-center/ — test email authentication

Up Next

Lesson 05 covers DMARC: the policy record that ties SPF and DKIM together, tells receiving servers what to do when checks fail, and gives you aggregate reports showing every source sending as your domain.