This article follows up on the previous ones
DKIM vs. SPF
If we set up SPF for a domain, the recipient can detect if someone else tries to send an email for that domain. Thus, we check all messages where the sender's address is from the given domain, and either the message was received from an authorized server or not and is likely forged.
When we use DKIM, the recipient checks the signature only for signed messages. A signature error occurs (most commonly in practice) when the sender decides to use this technology but sets it up incorrectly (for example, they're missing a public key in DNS or make a modification after signing the message). Or if someone were to modify it along the way. DKIM therefore doesn't help us recognize that someone else is trying to forge an email for a domain. We would have to allow only signed emails for that domain. In practice, the interesting information for us is that the email was correctly signed. However, this information doesn't easily reach the end user. Moreover, DKIM can correctly sign an email with a completely different domain than the one from which the message is sent.
DKIM and policies?
The original DomainKeys RFC 4870 (obsolete) contained a definition of a policy whether emails for a domain must be signed. Many DKIM guides describe this policy, even though the DKIM standard doesn't contain anything like it. The policy is written as a _domainkey TXT record in the DNS of the sending domain (example _domainkey.company.com). The content of the TXT record can be (other tags are also supported) o=- all messages must be signed or o=~ may be signed (default value).
Another option is the DKIM extension Author Domain Signing Practices (ADSP) described in RFC 5617, but today it's recommended to use DMARC, which we'll look at next time. ADSP also defines behavior using a _adsp._domainkey TXT record in the DNS of the sending domain (example _adsp._domainkey.company.com). The content of the TXT record is the tag dkim= and values unknown (signs some or all messages), all (all messages are signed) and discardable (all messages are signed, if not, the recipient should discard the message).
Principle of DKIM - DomainKeys Identified Mail
The DomainKeys Identified Mail (DKIM) method is described as an Internet standard in RFC 6376. The proposed standards RFC 8301 and RFC 8463 provide an update of the algorithms used to achieve better security (known vulnerabilities or weaknesses). DKIM was created by combining Yahoo's DomainKeys and Cisco's Identified Internet Mail techniques.
DKIM is quite different from SPF, but it also uses a DNS record, relying on the fact that the records are created by the domain owner. The signature is typically created by the sending mail server (or gateway). However, the address of the server sending or forwarding the message is not verified in any way. It's based on the assumption that whoever signed (created) the message owns the private key and is therefore authorized to send an email for that domain. If the message is forwarded by other servers, and is not modified, everything is fine.
A digital signature is used. Each email is supplemented in the header with an item containing the message signature using a private key. The corresponding public key for the domain is stored in a DNS record and anyone can use it to verify the message signature. Signatures are not directly visible to end users and are typically verified by the server. The check confirms that the message comes from an authorized source (given domain), and that it was not modified along the way.
The signature is added to the header item DKIM-Signature. It therefore doesn't affect the message in any way and support for this technology is not necessary for the recipient (along the way). It places no demands on end users who don't need to deal with electronic signatures, certificates, etc. Certificates are not used, so trustworthiness is not resolved through certification authorities. DNS is used for key trustworthiness (that only the owner can insert the record). Asymmetric cryptography is used for digital signatures, but the principle differs significantly from S/MIME or OpenPGP.
How digital signature works
Asymmetric cryptography uses two keys (a public one that is openly distributed, and a private one that only the owner knows). The keys are generated according to a certain cryptographic algorithm (such as RSA or ECC), from the private key we can calculate the public one. The best-known uses are for
- public key encryption - anyone can encrypt data with the public key, it can only be decrypted with the private key
- digital signature (authentication) - the private key is used to sign data, anyone can verify the validity using the public key
The digital signature works by calculating a hash for the data to be signed (using a certain hash function, such as SHA256) and then encrypting it with the private key. The resulting encrypted data is the digital signature (the signature also contains a timestamp of when the signing occurred). If the data is changed, the signature is invalid.
When verifying the signature, the signature (hash) is decrypted using the public key. Then a hash is calculated for the data (using the given function) and both hashes are compared. If both values are the same, the signature is valid.
Note: If data is encrypted with a private key, it can only be decrypted with a public key. And conversely, data encrypted with a public key can only be decrypted with a private key.
DKIM signing of mail messages
To use DKIM, we must first generate a pair of keys (private and public) according to a given algorithm. DKIM supports multiple digital signature algorithms. RSA-SHA256 must be supported and is recommended by the RFC 6376 standard. For hash calculation, SHA-256 is used and for encryption RSA, where it's necessary to choose a sufficient key length (it's also recommended to regularly change keys).
RFC 8301 prohibits the use of SHA-1, sets the minimum RSA key length to 1024 bits (previously 512) and recommends 2048 bits, and 4096 bits must be supported. A practical problem is that a long key doesn't fit into a DNS TXT record, which has a maximum length of 255 characters (it should be possible to split the key into multiple parts / lines). RFC 8463 adds the elliptic curve algorithm Edwards-Curve Digital Signature Algorithm (Ed25519) to the previously sole RSA. This allows secure use of shorter keys. For digital signatures, the Ed25519-SHA256 algorithm is available. For backward compatibility, it's possible to add multiple signatures to emails (and have multiple records with different selectors in DNS).
Note: In practice, RSA is still predominantly used and the key length is 1024 bits. For example, the latest version of Cisco Email Security documentation states that the safe and recommended key length is 768 - 1024 bits and doesn't support longer than 2048.
Signing is typically done by the sending mail server (or gateway), which owns the private key for the domain. It adds an item (Header Field) DKIM-Signature to the email header. This item should be treated as a tracing (Trace Header Field) and thus must be preserved and remain in the same order when transmitting the message. The value of DKIM-Signature is a list of tags separated by semicolons, i.e., individual items tag=value.
Used tags in DKIM-Signature
(P) means that the given tag must be present in the record (it's mandatory).
v- version (P) - DKIM specification version, so far1a- algorithm (P) - algorithm for generating the signature, typicallyrsa-sha256or newed25519-sha256b- signature data (P) - signature (of headers and data) in Base64bh- hash of body (P) - hash of the canonicalized part of the message body (may be limited by theltag)c- canonicalization - canonicalization algorithm for header/body, default issimple/simpled- domain (P) - message domain, the DKIM record is searched for in this domainh- header fields (P) - list of header items, separated by colons, which are signedi- agent or user identifier - agent or user identifier, typically @ and domain fromdl- length - length of the message body after canonicalization, from which the hash is calculated, default is the entire bodyq- query methods - list of query methods for obtaining the public key, default (and only option)dns/txts- selector (P) - selector for dividing the domain namespace in DNSt- timestamp - timestamp when the signature was createdx- expiration - date when the signature expiresz- copied header fields - may contain a copy of header item values, separated by |
Example of DKIM-Signature item in the header
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=seznam.cz; s=beta; t=1578641730; bh=/TD0lk+sB1gnEN1dbkIDJAt59PhptcTvRhtuO1OrpNs=; h=Received:From:To:Subject:Date:Message-Id:Mime-Version:X-Mailer:Content-Type; b=LfWqIXU+MPBXIgfdHqh5y6TGGK/avFzMX6Lu7mFKNE8sPSCIGV8Vy5iFIyUMqFvyj UnWwctBxpDJ8TuaCOHB3dMp0PD9CdmZGa/0vOJhFE/SxVHFVs/iUDR7HLvTvfG2PA9 hwLQxdZAuX4PIDOGTgERxJ/FB9fxb0H3ga7FPuno=
Canonicalization
When transmitting an email message, it should not be modified, yet semantically insignificant changes may occur (such as changes in letter case in the header, addition or removal of whitespace). In such a case, the signature would be invalid. DKIM offers the option to perform a certain defined data modification (canonicalization) before calculating the hash. We can specify that original data is used, parameter simple. Or modification is performed, value relaxed. The value is specified for header/body. The default is simple/simple, meaning without modifications.
Calculation of hashes for the message
Both signing and signature verification of a message are based on calculating two hashes. The Signer chooses the algorithm and parameters, the Verifier uses the parameters from DKIM-Signature. The first hash is calculated for the message body (using the specified canonicalization algorithm (c) and length possibly shortened according to (l)). It is then converted to base64 and either inserted or compared with the bh tag. The second hash is calculated for selected header items (the DKIM-Signature item with an empty (b) tag is always included, the specified canonicalization algorithm is applied to the data). The hash is then signed with the given algorithm (a) and inserted as the b tag.
We can also sign header items that are not present in the message. This secures the situation where someone might add a header along the way (we must consider that certain headers are supposed to be added en route). The RFC contains a list of items that are related to the message body and therefore good to sign:
- From (mandatory)
- Reply-To
- Subject
- Date
- To, Cc
- Resent-Date, Resent-From, Resent-To, Resent-Cc
- In-Reply-To, References
- List-Id, List-Help, List-Unsubscribe, List-Subscribe, List-Post, List-Owner, List-Archive
Some header items may occur multiple times, in which case the last instance is always signed (processed from bottom to top). If we want to sign multiple occurrences, we must list the item multiple times in the (h) tag. We can list an item multiple times even if it currently appears only once. This prevents its possible further addition.
Verification of DKIM signature of an email message
It is recommended to perform signature verification at the time of message receipt by the boundary MTA (not waiting until the end user accesses the message). The public key can be revoked at any time, so the signature would fail to verify even if it was valid at the time of delivery. The MTA can insert the verification result into a header item. The end user (email client) can use this item for message filtering.
Note: SPF can reject message reception already upon receiving the SMTP command MAIL FROM, whereas DKIM must receive the entire message.
How email message signature verification works
- upon message receipt, the server (verifier) checks if the header contains a
DKIM-Signatureitem - the
DKIM-Signaturein the message is read and parameters are obtained - using the selector (s) and domain (d), a DNS query is constructed and a TXT record for
s._domainkey.dis sought
Note: It strikes me that the domain specified in the signature is being verified. It may not have any connection to the domain in the sender's address.
- the public key (and possibly other parameters) is obtained from the DNS record
- according to the parameters (c, l, h, a), both hashes of the message are calculated
- first, the calculated hash for the message body is compared with the value read from the signature (bh), if it doesn't match, it returns PERMFAIL
- the signature is verified (b) against the calculated hash of the headers (the signature (b) is decrypted (a) with the public key, thus obtaining the hash of the headers, which is compared), if it doesn't match, it returns PERMFAIL
- if no error occurred, the signature was successfully verified, it returns SUCCESS (it was verified that the email is signed by the stated domain and the message was not modified)
Note: If verification fails, the message should not be rejected, but information should be provided on why the message's authenticity cannot be verified.
Evaluation of DKIM signature verification
The evaluation of each signature ends in one of three states
- SUCCESS - successfully verified
- PERMFAIL - verification failed or other permanent error
- TEMPFAIL - temporary error, for example, DNS query timeout
The result of signature verification should be passed on. One option is to add a new header item (before the existing DKIM-Signature). Authentication-Results seems suitable.
Public key record in DNS
DKIM supports multiple simultaneous public keys for one domain. To differentiate, a Selector is used, followed by the mandatory part _domainkey. The selector can be, for example, a location name or date (a dot is supported in it). This creates an FQDN (subdomain) on which we create a record for DKIM. For example, january.2020._domainkey.company.com. The record itself is published as a DNS TXT (type 16) Resource Record (RR). Its format, similar to DKIM-Signature, is composed of tags separated by semicolons.
Tags used in DKIM record
(P) means that the given tag must be present in the record.
v- version - version of the DKIM key record, if used, it must be first and containDKIM1h- hash algorithms - colon-separated list of accepted hash algorithms, default is all (sha256)k- key type - encryption algorithm, standard isRSA(or newed25519), means that the public key is stored as ASN.1 DER-encoded RSAPublicKey (encoded using base64)n- notes - possible notes for peoplep- public-key data (P) - the actual public key, if the value is empty, it means it has been revokeds- service type - for which services this record applies, so far only all*is possiblet- flags - flags, y - this domain is testing DKIM, s - if in DKIM-Signature there's an i tag, then the domain must be the same as in d (it must not be a subdomain)
Example of DNS record
"v=DKIM1;k=rsa;t=s;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDmzRmJRQxLEuyYiyMg4suA2SyMwR5MGHpP9diNT1hRiwUd/mZp1ro7kIDTKS8ttkI6z6eTRW9e9dDOxzSxNuXmume60Cjbu08gOyhPG3GfWdg7QkdN6kR4V75MFlw624VY35DaXBvnlTJTgRg/EW72O1DiYVThkyCgpSYS8nmEQIDAQAB"
To check the DNS DKIM record, we can use online services, such as DKIM record validator, DKIM Record Lookup, DKIM Record Check, DKIM Core Tools.
Practical deployment of DKIM
DKIM Verification
This is simple. We need a mail gateway (server) that supports DKIM (which Exchange Server is not). Enabling verification is then usually simple. The question is how to react to various test results. For a bad signature, we can add information to the message subject. It's better for users to know that the message was correctly signed, but it's not good to put this in the subject. Another solution is DMARC.
An example of a gateway with DKIM support is Cisco Email Security. I've briefly described enabling DKIM checking and message signing in the article Cisco Email Security - AntiSpam solution configuration.
DKIM Signing
We need a mail server or gateway that supports DKIM and will perform signing. It depends on what support this server has. It can generate keys for us and prepare a DNS record.
General steps we must take for signing
- determining algorithms and properties and generating keys
- determining the selector and creating a DNS record on our subdomain
- enabling message signing when sending
Key Generation
First, we need to generate a key pair and consider what algorithm we want to use (for now, probably unequivocally RSA) and what key length (for practice, probably 1024 bits or 2048 bits).
For key generation, we can use a variety of methods
- web service - from a security perspective, it's not a good solution because a third party has the keys
- signing application - the solution that will create the DKIM signature for us may also support key generation, for example, Cisco Email Security
- OpenDKIM - a set of tools for Linux
- OpenSSL - a well-known set of tools for Linux, also compiled for Windows
- PuTTYgen - a tool for generating keys, has a GUI on Windows (part of the PuTTY package)
On Linux, it's best to use OpenDKIM, on Windows you can very easily use OpenSSL for Windows.
Generating a private key
cd c:\Program Files\OpenSSL-Win64\bin openssl genrsa -out DKIM-private.key 1024
The resulting file looks like this:
-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDPtGH8e0rsuw7+Une1zYfuQ4taFz0dmQZqUVRaUN3ty+YLKMYS 2ntfRJsIjVGoEOVxunJMnTZwHNK2EhteyAMLSQ/sHFlE3couty1ExmsJHjeBdgBu vOubrxAID10lvUUTTTwCqryGwIac59NRaVNsqLfQJnghus1g7BUV9ASSNQIDAQAB AoGABxFGPEcdt4xt6C16MU97DppxxXEA/V7VnwyBaElUI+FKRJrwkneotwcol1Pn sWZRyFrlxMGctpfke5mGIOWBZPM3BBPxNrTwSc3S3SIy9IO34UMgE+omGKDWtz6u Go2BmkcRzGMngyaJ82igtTxae1KoNz8T35hFwQfwCju5osECQQDsJp+Kfkhy+XQk 6bRrO4AUydRDTpoPfPo2ZdtFY+yRa5lYNSiyugibldxiGBJS+Sh4/1Z1PsN1bajK tjCElkVdAkEA4Smq0umbDQaZRFqkqxM2iM8keQbkQ5ZC//jqvAr9hV/Qqtu/6j7X c5DJaQS7ouXl69AihtmMofnbwNE3MtUauQJAMTtwIXBobEfjVdq/OWfjMPJO5WVa qwX0KCkeCJ5ncH3NL12NyY0NRFp+4piAIXo+XNNm0/SszSt6eCB5hvrJJQJBAJzn MU/aVB7mm0VjuN4x/E2ns23XHJfwjO3dIo45RmN72mhFy93LPs4cdg4Fq0+fzvHd z0GTNgnlmHosEMAOepkCQQC1RCMPCIsKli3u6Wui5fNAO1FXGWXbcHgHOKFuKm2F jE09e3CoSkGOrgd2BTvGcO0v3DfNzHzzaUFIHjkMo9la -----END RSA PRIVATE KEY-----
Public key calculation
openssl rsa -in DKIM-private.key -pubout -out DKIM-public.key
The resulting file looks like this:
-----BEGIN PUBLIC KEY----- MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtGH8e0rsuw7+Une1zYfuQ4ta Fz0dmQZqUVRaUN3ty+YLKMYS2ntfRJsIjVGoEOVxunJMnTZwHNK2EhteyAMLSQ/s HFlE3couty1ExmsJHjeBdgBuvOubrxAID10lvUUTTTwCqryGwIac59NRaVNsqLfQ Jnghus1g7BUV9ASSNQIDAQAB -----END PUBLIC KEY-----
DNS record for DKIM public key
For the selected selector (dkim2020) and domain (company.com), for example dkim2020._domainkey.company.com, we create a TXT DNS record (Resource Record). We define its value using tags, only p is mandatory, where we insert the public key without the header, footer, and line breaks. We can temporarily use the t=y tag to indicate it's a test.
v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtGH8e0rsuw7+Une1zYfuQ4taFz0dmQZqUVRaUN3ty+YLKMYS2ntfRJsIjVGoEOVxunJMnTZwHNK2EhteyAMLSQ/sHFlE3couty1ExmsJHjeBdgBuvOubrxAID10lvUUTTTwCqryGwIac59NRaVNsqLfQJnghus1g7BUV9ASSNQIDAQAB
If we use a key length of 2048 bits, the public key is longer than 255 characters. We are running into the DNS record limit, where one text string can have a maximum of 255 characters. The solution is to split the text into multiple strings within one record. We can simply split it manually or use DNS record splitter.
v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVCeOGrI6wp36Gm6Ty0R/blq4tYFTzL/yLR8hbviefGL0syvN6P/kX83DTtg4VBr22+lGAMXvPQt+OU2scnMFu8ItyPd2VZdj/AEkW1Qb6EsOmwnHk67n2T4Neh16U8h7VYFdRyk24/zuLK3/V5rK6hIMvmfLsTw9R8CQkyj2o5f5xUrCNL55N9xIYy9sKCW9UV mH479TmIpvTVbygJKR0UxpVdKIpeP2rnv/xc4Nzq4vSuWdvFd7wPzYB/1TnmHYT6d8bKDJ4zhTQQyp7zY7Wax3JZngCHvQpBBLKCd5hqVoqb87siMAtKVkpMotvI+c5Ap4C+OUUQOcAnBL4KLdwIDAQAB
We can easily inspect the record, for example, using a command-line on Windows.
nslookup > set type=txt > dkim2020._domainkey.company.com Server: ns.company.com Address: 192.168.0.10 Non-authoritative answer: dkim2020._domainkey.company.com text = "v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPtGH8e0rsuw7+Une1zYfuQ4taFz0dmQZqUVRaUN3ty+YLKMYS2ntfRJsIjVGoEOVxunJM nTZwHNK2EhteyAMLSQ/sHFlE3couty1ExmsJHjeBdgBuvOubrxAID10lvUUTTTwCqryGwIac59NRaVNsqLfQJnghus1g7BUV9ASSNQIDAQAB"
Example of a record with a long key.
Non-authoritative answer: dkim2k._domainkey.firma.cz text = "v=DKIM1;k=rsa;p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtVCeOGrI6wp36Gm6Ty0R/blq4tYFTzL/yLR8hbviefGL0syvN6P/kX83DTtg4VBr 22+lGAMXvPQt+OU2scnMFu8ItyPd2VZdj/AEkW1Qb6EsOmwnHk67n2T4Neh16U8h7VYFdRyk24/zuLK3/V5rK6hIMvmfLsTw9R8CQkyj2o5f5xUrCNL55N9xIYy9s KCW9UV" "mH479TmIpvTVbygJKR0UxpVdKIpeP2rnv/xc4Nzq4vSuWdvFd7wPzYB/1TnmHYT6d8bKDJ4zhTQQyp7zY7Wax3JZngCHvQpBBLKCd5hqVoqb87siMAtKVkpMotvI +c5Ap4C+OUUQOcAnBL4KLdwIDAQAB"
To check the DNS DKIM record, we can use online services, such as DKIM record validator, DKIM Record Lookup, DKIM Record Check, DKIM Core Tools.
Signing messages using DKIM
If our mail server (gateway) supports DKIM, enabling signing is relatively simple. For example, on Cisco Email Security Configuring DomainKeys and DKIM Signing.
If we use Microsoft Exchange Server for sending, we're relatively unlucky because on-premises Exchange does not support DKIM (in contrast to cloud-based Exchange Online). But we can use a third-party AddOn (Transport Agent):
- Exchange DKIM Signer - often mentioned on the internet, LGPL license
- DkimX - commercial software with a trial version
- DKIM for Exchange Server and IIS SMTP Service - commercial software with a trial version
I've tried Exchange DKIM Signer, which is installed on Exchange as a Transport Agent. So it has full access to all the email messages it processes (it wouldn't work otherwise). So we must consider the trustworthiness of the installed plugin.
Brief documentation DKIM Signing Agent for Microsoft Exchange Server. The standard installation means
- download the latest version of DKIM-Exchange Releases
- from the package, run Configuration.DkimSigner.exe
- the Install button downloads a larger package from the web and installs libraries according to the Exchange Server version
- the installation is in C:\Program Files\Exchange DkimSigner, management is done using Configuration.DkimSigner.exe
- it installs as a Transport Agent, we can list it using
Get-TransportAgent, after installation it should be the last one, Exchange DkimSigner - when managing using the Configure button, we can verify that it is the latest
- on the DKIM Settings tab, we can add the header items to be signed, and set the algorithm and canonicalization, we choose the logging level for application
- on the Domain Settings tab, we set the individual domains for which messages should be signed, we can generate keys or load our own file, there is also a suggested DNS record for the public key

The Exchange DkimSigner agent does not sign messages in the TNEF format (Transport Neutral Encapsulation Format, also known as Outlook Rich Text Format). This format is supported by Microsoft Outlook (if the message is opened by an unsupported client, it usually sees the email with a winmail.dat attachment). The Exchange server commonly uses this format within the Exchange organization, but when sending to remote recipients (Remote Domain), it converts it to HTML or another format.
Online DKIM verification
Once we have configured DKIM message signing, we can use one of the services that will check that the messages are signed correctly. Either we send an email to the specified address and receive a report in the response, or we are shown a web page address to which we send the email, and we see the test result on the web. More properties are tested than just DKIM.
WoW jeden z mála profi článků ! Arigató !
Perfektní popis, díky