This how-to is created by the Dutch Internet Standards Platform (the organization behind [internet.nl](https://internet.nl)) and is meant to provide practical information and guidance on implementing DKIM.
DKIM stands for **D**omain**K**eys **I**dentified **M**ail and is described in [RFC 6376](https://tools.ietf.org/html/rfc6376) with updates in [RFC 8301](https://tools.ietf.org/html/rfc8301) and [RFC 8463](https://tools.ietf.org/html/rfc8463). It is meant to provide the owner of a domain with the means to claim that a message has actually been send by the domain's e-mail server and should therefore be considered legitimate. It works by signing every individual e-mail message with a specific key (private key), so that the receiving party can use a corresponding key (public key) published in the sending domain's DNS record to validate the e-mail authenticity and to check whether the e-mail has not been tampered with.
A common used technique used by spammers is to trick the receiving party into believing an e-mail is legitimate by using a forged sender address. This is also known as e-mail spoofing. DKIM has been designed to protect against spoofing. If an incoming e-mail does not have a DKIM signature or when it's DKIM signature does not validate, the receiving e-mail server should consider the e-mail to be SPAM.
* Use a DKIM key (RSA) of [at least 1024 bits](https://tools.ietf.org/html/rfc6376#section-3.3.3) to minimize the successrate of offline attacks. Don't go beyond a key size of 2048 bits since this is not mandatory according to the RFC.
* If a domain is not using e-mail (anymore), it is recommended to set an empty public key: "v=DKIM1; p=".
* When used with a specific selector, an empty public key means that e-mail signed with the associated public key must be considered unreliable since they public key was revoked.
* When used with a wildcard selector, setting an empty public key indicates that all previously used keys are revoked and must be considered unreliable. The owner of a domain can also use this to explicitly signal that a domain is not configured to use e-mail.
* [According to the RFC](https://tools.ietf.org/html/rfc6376#section-6.1.2) the absence of a selector / public key (e.g. as a result of deleting the entire DKIM resource record) is semantically equal to a resource record with an empty public key. This means that both approaches should be treated similar by the receiving mail server.
As mentioned in [RFC 6376 section 3.4](https://tools.ietf.org/html/rfc6376#section-3.4) some mail systems modify e-mail in transit. This type of modification is called canonicalization and is generally used to make things comparable before presenting the email to the signing or verification algorithm. You can imagine that this is important when signing and validating an e-mail; if things change too much this can invalidate a DKIM signature, which also impacts DMARC.
DKIM software allows you to specify the canonicalization settings. The settings used by the sender are set in the DKIM header of every e-mail using the "c" tag. Accepted values are "relaxed" and "simple" and since canonicalization exists for both the header and the body of an e-mail, the format used to represent the canonicalization setting is "value/value" for header and body respectively.
We currently advise against the "simple/simple" canonicalization setting because this (being the most strict setting) tolerates almost no modification of the header and body before signing, which is prone to cause problems when forwarding mail. This is confirmed in RFC 7960 [section 2.3](https://tools.ietf.org/html/rfc7960#section-2.3) and [section 4.1.1.2](https://tools.ietf.org/html/rfc7960#section-4.1.1.2). Therefore we recommend to use the "relexed/relaxed" setting which tolerates common modifications of the header and body before signing.
Notice:
* When not specified DKIM canoncalization defaults to "strict/strict".
* "c=strict" equals "c=strict/strict", but "c=relaxed" equals "relaxed/strict".
DKIM for outbound e-mail traffic can be accomplished by publishing a DKIM policy as a TXT record in a domain name's DNS zone, and by configuring the e-mail server to sign outbound e-mails.
Create the the file **/etc/opendkim/trusted_hosts** and make sure it contains the following:
127.0.0.1
localhost
example.nl
mail.example.nl
Now create the directory **/etc/opendkim/keys/example.nl** and execute the following command with this directory and make sure to replace 'YYYYMM' with the number of the current year and month. For example: "selector201906". This makes it easier to determine the age of a specific key in a later stage.
`opendkim-genkey -s selectorYYYYMM -d example.nl`
There are now 2 files in **/etc/opendkim/keys/example.nl** (the key pair):
* selector201906.txt: this file contains DNS complete DKIM DNS record including the public key.
> selector201906._domainkey IN TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCooJQftNOg3wOqVW5wOpr1PhhzgeP1IE9dTOtpUOCENP+z1HwP+8fFp9aGo/EKHoDQRhDUxXlVfocmRjb0lyjHD5ax16BBKLAd8+AgHZt1er8fmm2cL+7nurv0vU5YBG9LGUklD9qO/zJrIz+Lp+YO7D2rt0qYAgGzUOLJBWLBNQIDAQAB" ; ----- DKIM key selector201906 for example.nl
* selector201906.private: this file contains the private key which is going to be used by Postfix to sign all outbound e-mails.
Now make sure that the private key can only be read by the user opendkim by executing the following command:
`chown opendkim:opendkim selector201906.private`
The next step is to create the key table file **/etc/opendkim/key_table**. This file will tell opendkim about the domains that have been configured and where to find their keys. Add the following to configure example.nl:
OpenDKIM does not support the automated rotation of DKIM keys. This means that you should rotate your keys manually, write a script to do this, or use an existing script like [https://github.com/tetsuo13/OpenDKIM-Rotate-Keys](https://github.com/tetsuo13/OpenDKIM-Rotate-Keys) or [https://github.com/captbrando/dkimrotator](https://github.com/captbrando/dkimrotator).
The first line publishes the selector and the associated public key. The second line tells receiving mail server that all e-mail coming from the domain example.nl are DKIM signed.
### Configure Postfix
The final step is to configure Postfix to actually sign outbound e-mail using OpenDKIM. In order to do this add the following to **/etc/postfix/main.cf**:
milter_protocol = 6
milter_default_action = accept
smtpd_milters = inet:localhost:12301
non_smtpd_milters = inet:localhost:12301
When you are ready to start using DKIM restart Postfix, but make sure you waited long enough for the DKIM DNS record to succesfully propagate.
SpamAssassin uses a scoring mechanism in order to determine if an e-mail should be considered spam. By default SpamAssassin considers an e-mail to be spam if the score at least "5". An e-mail starts with a score of 0 and points are added based on the [tests](https://spamassassin.apache.org/old/tests_3_3_x.html) performed. The tests performed can be configured by adding specific [configuration parameters](https://spamassassin.apache.org/full/3.4.x/doc/Mail_SpamAssassin_Conf.html) in **/etc/spamassassin/local.cf**.
Now here's the tricky part. The points added to the score of an incoming e-mail based on the results of a specific test, is at its core a custom job. Many variables can be taken into consideration when scoring an e-mail (which is considered the strength of a post-SMTP spam filter) and the detailed scoring depends on a domain owner's specific wishes. For the sake of this how-to, the DKIM scoring will be based on the assumption that the domain owner wants to consider an e-mail to be spam if the sending e-mail server's DKIM signature is not valid.
- Click Add and then select the newly created private key in the overview, then click Details on top of the page.
![](images/dkim-halon-private-key.png)
On the detail page you see the public key you just generated and the button "DKIM record".
![](images/dkim-halon-public-key-details.png)
Click the "DKIM record" button and give your Domain and Selector which you want to use. Hit "Generate" and here you see the DKIM record which you can use in your DNS server.
![](images/dkim-halon-dns-record.png)
Publish the DNS record for the domain in your DNS environment.
## Outbound signing
Navigate to `Configuration -> Code editor` select there the End of Data (EOD/EOD rcpt) script where you want to use the DKIM signing.
Before the `GetMailMessage()->queue()` add:
```php
$dkimselector = "selector201909"; // Selector
$dkimdomain = "example.nl"; // Header From:
$dkimcertificate = "selector201909"; // certificate ID
if (GetMailMessage()->signDKIM($dkimselector,$dkimdomain,"pki:".$dkimcertificate) == none)
Defer("DKIM signing error, bla bla error message");
```
The following syslog message is visible in the logging if DKIM signing is successful.
```
[6xxxxxxb-dxxf-1xx9-bxx5-0xxxxxxxxx4] [EOD] DKIM signed for example.nl (selector201909) with signature b=Yxxxxx9
```
## Inbound email
Navigate to `Configuration -> Code editor` select there the End of Data (EOD/EOD rcpt) script where the incoming mail is checked.
Before the `GetMailMessage()->queue()` add:
```php
foreach (GetMailMessage()->getHeaders("DKIM-Signature", ["field" => true]) as $i => $dkimsign) {