CertaDNS
Skip to lesson

What a Signature Covers · lesson 2 of 3

DKIM-Signature, tag by tag

After this lesson you can

Read any DKIM-Signature header and state what each tag contributes to the verification.

Assumes you have read The two hashes.

Every tag in a DKIM-Signature header either tells the verifier where to find something or constrains what it will accept. Read one properly and you can predict the result before the verifier produces it.

A signature, annotated

(1)DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
(2) d=example.com; s=mail202609;
(3) h=from:to:subject:date:message-id;
(4) bh=2jmj7l5rSw0yVb/vlWAYkK/YBwk=;
(5) t=1757894400; x=1758499200;
(6) b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSb...
  1. v=1 is the only version there has ever been. a= is the algorithm, and c= is header/body canonicalisation.
  2. d= is the signing domain — the one DMARC compares against the From header. With s= it gives the DNS name to query: mail202609._domainkey.example.com.
  3. h= is the ordered list of headers covered. Order matters: the verifier canonicalises them in exactly this sequence.
  4. bh= is the body hash. This is the value compared in step one of verification.
  5. t= is the signing timestamp and x= an expiry. Both optional, and x= is rare.
  6. b= is the signature itself. During verification this field is treated as empty, because it cannot cover itself.

The tags worth knowing

TagMeaningNotes
d=Signing domainThe one that matters for DMARC alignment.
s=SelectorWith d=, forms the DNS name of the key.
a=Algorithmrsa-sha256 in practice. rsa-sha1 is dead — RFC 8301 forbids it.
c=Canonicalisationheader/body. Almost always relaxed/relaxed.
h=Signed headersOrdered. Repeating a name oversigns it.
bh=Body hashChecked first, separately.
b=SignatureBlanked while verifying itself.
l=Body lengthSigns only the first N octets. Rarely a good idea.
i=IdentityAn address within d=. Informational for DMARC.
x=ExpiryAfter this, verifiers may treat the signature as invalid.
q=Query methodOnly dns/txt is defined.

d= is not necessarily the domain in the From header

A message can carry From: billing@yourbank.com and a perfectly valid signature with d=mailer.somevendor.com. DKIM passes. DMARC does not, because d= and the From domain are unrelated. When a vendor says “we sign everything with DKIM”, this is the question to ask them.

More than one signature

A message may carry several DKIM-Signature headers, and each is verified independently. This is normal: a platform signs as itself, and a gateway adds a second signature as your domain on the way out.

  • A verifier reports a result per signature.
  • DMARC needs only one of them to pass and align.
  • A failing signature alongside a passing aligned one is not a problem to chase.
Why b= has to be blanked during verification

The signature covers the DKIM-Signature header, because otherwise an attacker could rewrite h= or d= at will. But it cannot cover its own value — the value does not exist until the computation finishes. RFC 6376 §3.7 resolves it by defining the header as signed with b= treated as empty, on both sides. Every other tag in the header is genuinely covered.

Try it on a real domain

Free, no account, public DNS only.

Last reviewed