Skip to content

DKIM verification with AWS SES Easy DKIM

How AWS SES proves you own a sending domain: three CNAME tokens published to your DNS host let SES sign outbound mail with DKIM on your behalf.

Updated May 25, 2026

The first thing any serious mail provider asks of a new sender is “prove you own the domain.” For Amazon SES the proof is DKIM — published once, then used to sign every outbound message so the receiver can verify it really came from you.

Concept

DKIM (DomainKeys Identified Mail)

An email authentication standard where the sender signs outgoing messages with a private key, and receivers verify the signature against a public key published in DNS.

DKIM ties a message to a domain. The signing key lives with the sender; receivers fetch the verification key by doing a TXT lookup at {selector}._domainkey.{domain}. With SES Easy DKIM that lookup chains through a CNAME you publish into a TXT record AWS hosts — the receiver doesn’t care about the indirection, only that a public key lands at the expected name. A failed DKIM check is one of the strongest spam signals there is, which is why every legitimate sender publishes one.

How DKIM works (the general flow)

Before drilling into the AWS SES specifics, this is the general DKIM flow as receivers see it. The sender signs outgoing messages with its private key; the receiver pulls the DKIM-Signature header off the message, fetches the matching public key from DNS, re-derives the body digest, and accepts or rejects based on whether they agree. Easy DKIM doesn’t change this flow — it changes who hosts the public-key TXT record (covered in the next section).

DKIM signature verification

The canonical DKIM diagram: the sender signs an outgoing email with its private key; the receiver pulls the DKIM signature out of the headers, fetches the matching public key from DNS, re-derives the digest, and either passes or fails based on whether they agree.

0 / 8
Send DKIM-signed EmailReceive EmailExtract dkim-signaturefrom email headersFetch example.com DKIM recordat 's._domainkey.example.com'Return example.comDKIM recordYesNoSending MailServerPrivate DKIM keyDKIM-SIGNATUREincluded in headersReceiving Mail Serverd-example.coms=s.domainkeyexample.comDNSValidate message using publickey from DKIM DNS entryWas messageunchanged?DKIM PassDKIM Fail

Easy DKIM: AWS holds the keys, you publish the CNAMEs

With “Easy DKIM”, you never see the private key. SES generates the keypair, keeps the private half, and tells you three DNS-friendly tokens. You publish three CNAME records that point each token’s _domainkey subdomain at SES’s own DKIM record. SES then resolves the chain at verification time and confirms ownership.

  1. Create the domain identity in SES with Easy DKIM enabled

    From the SES console or CLI, register example.com as a domain identity. SES generates three DKIM selectors immediately.

  2. Read the three tokens off the SES response

    The response contains a DkimTokens array. Each token is the DKIM selector you’ll use in the CNAME name.

    ses:get-identity-dkim-attributes response
    {
      "DkimAttributes": {
        "example.com": {
          "DkimEnabled": true,
          "DkimVerificationStatus": "Pending",
          "DkimTokens": [
            "hirjd4exampled5477y22yd23ettobi",
            "v3rnz522czcl46quexamplek3efo5o6x",
            "y4examplexbhyhnsjcmtvzotfvqjmdqoj"
          ]
        }
      }
    }
  3. Publish three CNAMEs on the domain's DNS host

    Each CNAME points {token}._domainkey.{your-domain} at {token}.dkim.amazonses.com.

    TypeName ({token}._domainkey.{verifiedDomain})Value ({token}.dkim.amazonses.com)
    CNAMEhirjd4exampled5477y22yd23ettobi._domainkey.example.comhirjd4exampled5477y22yd23ettobi.dkim.amazonses.com
    CNAMEv3rnz522czcl46quexamplek3efo5o6x._domainkey.example.comv3rnz522czcl46quexamplek3efo5o6x.dkim.amazonses.com
    CNAMEy4examplexbhyhnsjcmtvzotfvqjmdqoj._domainkey.example.comy4examplexbhyhnsjcmtvzotfvqjmdqoj.dkim.amazonses.com
  4. Wait for propagation, then re-check the status

    DNS can take up to 72 hours to propagate globally. SES re-polls automatically. When all three CNAMEs resolve, DkimVerificationStatus flips to Success and SES will start signing outbound mail with the corresponding selectors.

The setup, on the wire

How a receiver verifies a signed message

Once SES is signing, every outbound message carries a DKIM-Signature header. Here’s what one looks like on the wire — every tag in it drives a specific step of the verification:

DKIM-Signature header on a SES-signed message
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple;
  d=example.com; s=hirjd4exampled5477y22yd23ettobi;
  h=From:To:Subject:Date:MIME-Version:Content-Type;
  bh=4WzxBcb9pYDhKQNbN+yYR4ydyEaGAm3MK6+u7gRkc6E=;
  b=Vy7xZA92Q5n2Pp...base64...wL3kEa+f1Z4U=
TagWhat it carriesHow the receiver uses it
v=DKIM version (always 1)Sanity check; reject unknown versions
a=Signing algorithm (rsa-sha256)Picks which verifier to instantiate
c=Canonicalization for headers/body (relaxed/simple)Knows how to re-canonicalize before hashing
d=Signing domain (example.com)First half of the DNS lookup name
s=Selector (one of the three Easy DKIM tokens)Second half of the DNS lookup name
h=Colon-separated list of headers that were signedReconstructs exactly which headers go into the signature input
bh=Base64 SHA-256 of the canonicalized bodyCompared against the receiver’s own recomputation
b=Base64 RSA signature over the canonicalized signed headersRSA-verified against the public key from DNS

The flow below walks through the verifier doing this against an SES Easy DKIM setup. Step labels in the diagram are short; the body of each step (visible when active in the list below) names the artifact and what the receiver actually computes.

Receiver-side DKIM verification of a SES-signed message

Same shape vocabulary as the canonical diagram above, zoomed in on the SES-specific mechanics: the Resolver and the AWS-hosted TXT are split into two distinct actors so the CNAME chase from your DNS into amazonses.com is visible. The colored edge animates the active step; edge labels carry the data that crosses each wire.

0 / 10
SignDeliverParse d=, s=TXT lookupFollow CNAMEv=DKIM1; k=rsa; p=...Parsed public keyVerify body hashand signatureYesNoSES (sender)Private DKIM keySigned emailReceiverResolverAWS-hosted TXTBody hash ANDsignature OK?DKIM PassDKIM Fail

What the verifier actually computes, in pseudocode:

Receiver-side DKIM verification (pseudocode)
sig    = parse_dkim_signature_header(message)
domain = sig["d"]                                          # example.com
selector = sig["s"]                                         # hirjd4...
pubkey_txt = dns.txt(f"{selector}._domainkey.{domain}")     # follows CNAME -> amazonses.com TXT
pubkey = parse_dkim_pubkey(pubkey_txt)                      # extract p= and decode base64

# Body check
canon_body = canonicalize_body(message.body, sig["c"].split("/")[1])
if sha256(canon_body) != base64_decode(sig["bh"]):
    return "dkim=fail (body hash mismatch)"

# Header check
signed_headers = pick_headers(message, sig["h"].split(":"))  # e.g. From, To, Subject, Date, ...
canon_headers  = canonicalize_headers(signed_headers, sig["c"].split("/")[0])
input_bytes    = canon_headers + dkim_signature_with_b_blanked(sig)
if not rsa_verify(pubkey, input_bytes, base64_decode(sig["b"])):
    return "dkim=fail (signature mismatch)"

return f"dkim=pass header.i=@{domain} header.s={selector}"

Why three CNAMEs, not one

Three independent selectors give SES room to rotate signing keys server-side without ever asking you to touch DNS. At any given moment, SES signs with one of the three selectors; the other two slots stand by holding either old key material (so in-flight messages still verify) or new key material being phased in. When rotation completes, the active selector swaps. Because the CNAMEs point at TXT records AWS owns under dkim.amazonses.com, the rotation is entirely opaque to you. Don’t try to “tidy up” by removing two of the three CNAMEs — you’ll lose mail the moment SES rotates to a selector your DNS no longer chains to, and the failures will be DKIM verification failures (not DMARC alignment failures, which is a separate axis).

DKIM is one half of a complete sending setup. The other half is making SPF align under DMARC, which means controlling the SMTP envelope sender (the MAIL FROM, which receivers expose as the Return-Path header) — and that lives in 0020 Custom MAIL FROM domain in AWS SES · DevOps .

Reference