Explanation DKIM (DomainKeys Identified Mail) in all details
DKIM (DomainKeys Identified Mail) is a system that lets your official mail servers add a signature to headers of outgoing email and identifies your domain’s public key so other mail servers can verify the signature. As with SPF, DKIM helps keep your mail from being considered spam. It also lets mail servers detect when your mail has been tampered with in transit.
To understand how DKIM works is to know the difference between email envelope and email header.
A sender creates the DKIM by signing the email with a digital signature. This signature is located in the email header. The sending mail transfer agent (MTA) generates the signature by using an algorithm applied to the content of the signed fields. This algorithm creates a unique string of characters, or a hash value.
When the MTA generates the signature, the public key used to generate it is stored at the listed domain. After receiving the email, the recipient MTA can verify the DKIM signature by recovering the signer’s public key through DNS. The recipient MTA then uses that key to decrypt the hash value in the email’s header and simultaneously recalculate the hash value for the mail message it received. If these two keys match, then the email has not been altered, giving users some security knowing that the email did originate from the listed domain, and that nothing has modified it since it was sent.
DKIM (DomainKeys Identified Mail) together with Sender Policy Framework (SPF) are foundation stones for DMARC.
DKIM in all details
Step 1: Identifying what message elements to sign with DKIM
First, a sender decides which elements of the email they want to include in the signing process. They can decide to include the whole message (header and body) or just focus on one or more fields of the email header. The elements they decide to include in their DKIM signing process must remain unchanged in transit, or the DKIM signature will fail authentication.
For example, if an email is forwarded from Yahoo to Gmail, Yahoo may add a line of text at the top of the email (e.g. "forwarded by Yahoo mail"). At that point, the body of the email has been changed and, if the body was included in the DKIM signing process, the DKIM authentication will fail for the forwarded email.
However, if only an element of the header, such as the From: field was included in the DKIM signature, and the message was forwarded from Yahoo to Gmail, the DKIM authentication would pass, since the part of the message that was changed was not signed by DKIM.
Step 2: The encryption process
So what does this signing process look like? Cryptography is at the center of it. The sender will configure their email platform to automatically create a hash of the parts of the email they want signed. The hashing process converts readable text into a unique textual string.
Before sending the email, that hash string is encrypted using a private key. The private key is assigned to a unique combination of domain and selector, allowing you to have multiple legitimate private keys for the same domain (which is important for email governance and security purposes). Only the sender has access to the private key.
After the encryption process is complete, the email is sent.
Step 3: Validating the DKIM signature with a public key
The email provider receiving the email sees that it has a DKIM signature, which reveals which domain/selector combination signed the encryption process. To validate the signature, the mailbox provider will run a DNS query to find the public key for that domain/selector combination.
This public key has the unique characteristic that it is the only match for the private key that signed the email, also known as a keypair match. The keypair match enables the email provider to decrypt the DKIM signature back to the original hash string.
The email provider then takes the elements of the email signed by DKIM and generates its own hash of these elements. Finally, the mailbox provider compares the hash it generated with the decrypted hash from the DKIM signature. If they match, we know that
- The DKIM domain really does "own" the email, otherwise the decryption process wouldn’t have worked in the first place
- The elements of the email signed by DKIM were not changed in transit (if they were changed, the hashes would not match)
Why it matters: Email providers who validate DKIM signatures can use information about the signer as part of a program to limit spam, spoofing, and phishing, although DKIM does not tell receivers to take any specific actions. Depending on the implementation, DKIM can also ensure that the message has not been modified or tampered with in transit.
How Can I Read the DKIM Header?
Here is an example DKIM signature (recorded as an RFC2822 header field) for the signed message:
DKIM-Signature: v=1; a=rsa-sha1; q=dns/txt; d=example.com; firstname.lastname@example.org; s=jan2019.eng; c=relaxed/simple; t=1117574938; x=1118006938; h=from:to:subject:date; b=dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR
Let’s take this piece by piece to see what it means. Each "tag" is associated with a value:
v= Version. This is always 1
a= the signing algorithm. It will usually be either rsa-sha1 or rsa-sha256. Other algorithms are possible but not always supported. The signing algorithm is rsa-sha1.
q= The q field defines the query method(s) used to retrieve the DKIM public key. It doesn’t have to be present and defaults to dns/txt. Currently dns/txt is the only supported method but other methods may be added at a later date.
d= the signing domain. The signing domain is example.com. This is the domain that sent (and signed) the message.
i= This is the identity of the user or program for which the message has been signed. This can be omitted but if present the domain must match
dalthough the username before the @ may be dropped.
s= the selector. The selector is jan2019.eng.
c= the canonicalization algorithm(s) for header and body. The canonicalization algorithm(s) for header and body are relaxed/simple.
t= the signature timestamp. This is when it was signed. The signature timestamp is 1117574938.
x= the expire time. Because an already signed email can be reused to "fake" the signature, signatures are set to expire. The expire time is 1118006938
h= the list of signed header fields, repeated for fields that occur multiple times. This is the list of fields that have been signed to verify that they have not been modified. The list of signed header fields includes from:to:subject:date
b= the actual digital signature of the contents (headers and body) of the mail message. This is the heart of the DKIM signature itself. Everything else up to this point has been meta information on how this value was calculated. The digital signature is dzdVyOfAKCdLXdJOc9G2q8LoXSlEniSbav+yuU4zGeeruD00lszZVoG4ZHRNiYzR.
bh= the body hash. In our example body hash is not listed. This is the base64 encoded hash of the message body after it has been canonicalized via the method in
cand then run through the hash function in
l= the length of the canonicalized part of the body that has been signed. The signing domain can generate a key based on the entire body or only some portion of it. That portion would be listed here. The length of the canonicalized part of the body that has been signed is not listed.
bpart of DKIM header is the heart of the DKIM signature itself. Everything else up to this point has been meta information on how this value was calculated.
A receiving SMTP server wanting to verify uses the domain name and the selector to perform a DNS lookup. For example, given the example signature above: the
d tag gives the author domain to be verified against, example.com ; the
s tag the selector, jan2019.eng. The string
_domainkey is a fixed part of the specification. This gives the TXT resource record to be looked up as:
Another words: Using the domain value of example.com, we know that domain keys are located as subdomains of _domainkey.nickcanzoneri.com. The selector value of jan2019.eng gives us the exact sub domain to use as
The data returned from the DNS query of this record is also a list of tag-value pairs. It includes the domain's public key, along with other key usage tokens and flags as in this example:
"k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDDmzRmJRQxLEuyYiyMg4suA2Sy MwR5MGHpP9diNT1hRiwUd/mZp1ro7kIDTKS8ttkI6z6eTRW9e9dDOxzSxNuXmume60Cjbu08gOyhPG3 GfWdg7QkdN6kR4V75MFlw624VY35DaXBvnlTJTgRg/EW72O1DiYVThkyCgpSYS8nmEQIDAQAB"
The receiver can use the public key (value of the
p tag) to then decrypt the hash value in the header field, and at the same time recalculate the hash value for the mail message (headers and body) that was received. If the two values match, this cryptographically proves that the mail was signed by the indicated domain and has not been tampered with in transit.
Relationship to SPF and DMARC