DKIM signing is surprisingly not about proving your email is legitimate to the recipient; it’s about proving it’s legitimate to the receiving server.

Let’s see this in action. Imagine you’re sending an email from sender@example.com to recipient@otherdomain.com.

# On your sending mail server (e.g., Postfix, Sendmail)
# Assume 'opendkim' is installed and configured to sign for 'example.com'

# A typical outbound email might look like this (simplified headers):
#
# From: Sender Name <sender@example.com>
# To: Recipient Name <recipient@otherdomain.com>
# Subject: Test Email
# Date: Tue, 15 Oct 2024 10:00:00 -0700
# Message-ID: <some-unique-id@example.com>
#
# This is the body of the email.

When your mail server processes this for sending, opendkim (or your chosen DKIM signing agent) intercepts it. It takes the headers and body, applies a cryptographic hash (usually SHA256), and then encrypts that hash using your private key. This encrypted hash becomes the DKIM-Signature header.

# The mail server adds this header *before* sending:
#
# From: Sender Name <sender@example.com>
# To: Recipient Name <recipient@otherdomain.com>
# Subject: Test Email
# Date: Tue, 15 Oct 2024 10:00:00 -0700
# Message-ID: <some-unique-id@example.com>
# DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=example.com; s=selector1;
#  t=1697370000; bh=some_base64_encoded_hash_of_body_and_selected_headers;
#  h=From:To:Subject:Date:Message-ID; b=some_base64_encoded_signature_of_bh_and_h;
#
# This is the body of the email.

The DKIM-Signature header contains several key pieces:

  • v=1: The DKIM version.
  • a=rsa-sha256: The signing algorithm.
  • c=relaxed/simple: The canonicalization method for headers and body. relaxed allows minor whitespace and line ending changes, simple requires exact matches.
  • d=example.com: The signing domain.
  • s=selector1: The selector, a short string that helps the recipient find the correct public key.
  • t=1697370000: A timestamp (optional but good practice).
  • bh=...: The hash of the canonicalized body.
  • h=From:To:Subject:Date:Message-ID: The list of headers that were included in the signature calculation.
  • b=...: The actual signature, encrypted with your private key.

The receiving server, otherdomain.com, sees this DKIM-Signature header. It uses the d (domain) and s (selector) values to look up your public key. It queries DNS for a TXT record at selector1._domainkey.example.com. This record contains your public key and information about the signature.

The recipient server then:

  1. Verifies the signature’s integrity using the public key and the bh (body hash) and h (headers) fields.
  2. Checks if the d (domain) in the signature matches the From: header’s domain.
  3. Checks if the s (selector) is valid.
  4. Calculates its own hash of the email’s body and selected headers using the same canonicalization methods (c=relaxed/simple).
  5. If its calculated hash matches the bh in the signature, and the signature decrypts correctly with the public key, the email is considered DKIM-valid for example.com.

This process doesn’t guarantee the email is from a legitimate sender, but it guarantees that the email hasn’t been tampered with since it was signed by the server at example.com. It’s a crucial piece of the modern email authentication puzzle, working alongside SPF and DMARC.

To generate keys and set up signing, you’ll typically use a tool like opendkim-genkey.

First, create a directory for your keys:

sudo mkdir -p /etc/opendkim/keys/example.com
cd /etc/opendkim/keys/example.com

Then, generate the key pair. You need to specify a selector (e.g., mail or selector1) and your signing domain (example.com).

sudo opendkim-genkey -b 2048 -d example.com -D . -s mail
  • -b 2048: Specifies a 2048-bit key length, which is a good balance of security and performance.
  • -d example.com: The domain for which you are signing.
  • -D .: The directory to output the keys into (the current directory).
  • -s mail: The selector name. This will result in files named mail.private (your private key) and mail.txt (the public key DNS record).

Next, set the correct ownership and permissions for the private key so only the DKIM signing process can read it.

sudo chown opendkim:opendkim mail.private
sudo chmod 600 mail.private
  • chown opendkim:opendkim: Ensures the opendkim user and group own the private key file. Your mail server’s DKIM milter will run as this user.
  • chmod 600: Gives read/write permissions only to the owner, preventing any other user or process from accessing it.

Now, you need to publish the public key in your DNS. The content of the mail.txt file is what you’ll add as a TXT record. It will look something like this:

mail._domainkey IN TXT ( "v=DKIM1; h=sha256; k=rsa; "
  "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0f..."
  "..."
  "AQAB" ) ; ----- DKIM key mail for example.com

You’ll create a TXT record in your DNS zone for example.com with the name mail._domainkey. The value will be the quoted string from the mail.txt file. Some DNS providers automatically handle the quoting and concatenation of long strings.

Finally, configure your mail server (e.g., Postfix) to use the DKIM milter. In your main.cf (for Postfix), you’d add lines like:

# /etc/postfix/main.cf
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
milter_protocol = 6

And in your OpenDKIM configuration file (e.g., /etc/opendkim.conf):

# /etc/opendkim.conf
Domain                  example.com
KeyFile                 /etc/opendkim/keys/example.com/mail.private
Selector                mail
Canonicalization        relaxed/simple
Socket                  inet:8891@localhost
  • Domain, KeyFile, Selector: These tell OpenDKIM which key to use for which domain.
  • Canonicalization: Crucial for compatibility. relaxed/simple means headers are canonicalized with relaxed rules and the body with simple rules.
  • Socket: This defines how the milter listens for connections from the mail server. inet:8891@localhost means it listens on TCP port 8891 on the local machine.

After restarting your mail server and the OpenDKIM service, outbound emails from example.com will be signed.

The next hurdle you’ll likely face is understanding how DMARC policies interact with DKIM failures, especially when dealing with forwarded emails.

Want structured learning?

Take the full Smtp course →