DKIM Multi-Signing with Exim

Exim supports DKIM double-signing or multi-signing of outgoing messages. It is thus possible to sign an email with both, RSA and Ed25519. This article shows how to set it up.

Generate RSA Key

# Write 2048-bit private RSA key to disk
openssl genrsa -out rsa-test.private.pem 2048

# Print RSA public key on console
PUB=`openssl rsa -in rsa-test.private.pem -pubout -outform DER | openssl base64 -A`
echo "v=DKIM1; k=rsa; p=$PUB"

# Result
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvWjzSGjz0TE3O1nDJc9gTG2KRxLAhhFd8GRHTZxTcV88GH/gtRBEtv6mxFucfhmwJJbLGK3hHCNmwguG64HzOtYJkdklUC7f7TlvCsYk28ZLLehVEk5gOK0OTTlEVmFKHbJiyZnUr30x7Qb6HF9z7oyGwu9JshVBLYeVsxfZu1Hvk85XHgmdoCVtsY/phNeTpK4mHQIYYfrvliWAjGs2l0QRjEIo9UxVNYLi0VeIq3gkUD+x/scxdU5Ip+kwns9SqHqaO6mOGCu7aAeTY1SrFCla6abxsuMLY4BbFyWyqrYzR9fhfCBsbN5lOVv+qI24lKSm9gxe0AMoTjTVhMj2WQIDAQAB

Save the result in a DNS TXT record under rsa-test._domainkey.example.net, where rsa-test is the selector and example.net is your mail domain.

Generate Ed25519 Key

# Write private Ed25519 key to disk
openssl genpkey -algorithm ed25519 -out ed-test.private.pem

# Print Ed25519 public key on console
PUB=`openssl pkey -in ed-test.private.pem -pubout -outform DER | tail -c 32 | openssl base64 -A`
echo "v=DKIM1; k=ed25519; p=$PUB"

# Result
v=DKIM1; k=ed25519; p=3SUqa9UbfciWkk7tlcJ9P1VD5pXAasg0JUn/OgjVbKE=

Note that openssl outputs a 44-byte ASN.1 structure, whereas DKIM requires only the 32-byte raw key at the end of it. We thus use “tail” to print the last 32 bytes. Save the result in a DNS TXT record under ed-test._domainkey.example.net, where ed-test is the selector and example.net is your mail domain.

Exim Config

The following config snippet exemplifies how to configure DKIM signing in the transport section of Exim:

remote_smtp:
  debug_print = "T: remote_smtp for $local_part@$domain"
  driver = smtp

  # Load domain/selector configuration from file /etc/exim4/dkim.conf
  dkim_domain = ${lookup{$sender_address_domain}lsearch*@{/etc/exim4/dkim.conf}{$sender_address_domain}{}}
  dkim_selector = ${extract{selector}{${lookup{$sender_address_domain}lsearch*@{/etc/exim4/dkim.conf}}}{$value}{}}

  # Load private key from directory /etc/exim4/dkimkeys/ subject to $dkim_selector
  # Additionally, you may want to use $dkim_domain for file/directory selection
  dkim_private_key = /etc/exim4/dkimkeys/$dkim_selector.private.pem

  # Relaxed canonicalization is recommended (c = relaxed/relaxed)
  dkim_canon = relaxed
  
  # true = fail on signing error, e.g., due to errors when loading key
  # false = ignore signing errors and continue message delivery
  dkim_strict = true

  # List of headers to sign
  dkim_sign_headers = Subject:Content-Transfer-Encoding:Content-Type:In-Reply-To:From:Cc:References:To:MIME-Version:Date:Message-ID:Sender:Reply-To

The following example shows how /etc/exim4/dkim.conf may look like:

example.net: selector=rsa-test:ed-test

Add a line for every mail domain that supports DKIM signing. Domains not listed will not be signed. The selector field is a colon-separated list of keys used for signing. In this example, we use two keys, which will result in two DKIM-Signature headers.

The private key is selected via the configuration option “dkim_private_key”, while the public key must be available as a TXT record in DNS under $dkim_selector._domainkey.$dkim_domain. The directory /etc/exim4/dkimkeys/ contains the private keys in PEM format. Files must be readable by Exim. The public keys are not required for signing.

Test It

Test your signing setup with the DKIM Test. The test supports RSA and Ed25519.