I spent a good part of the day today working to make sure that a DomainKeys and DKIM server was properly signing messages. Here’s some of the confusing stuff I sorted out:
First off DomainKeys and DKIM are different slightly different technologies that do the same thing. My understanding is that Yahoo, Gmail, and others came up with DomainKeys as a solution to to solve email forgery. The EITF then tweaked the original DomainKeys spec a little and made their version called DKIM (DomainKeys Identified Mail). The two are very similar and I haven’t found anything that describes their technical differences.
First, I guess it helps to know basically how DomainKeys works. Essentially the administrator creates a Public/Private key pair on the mail server. The private key is used to sign the message while the public key is posted as a DNS TXT record for receiving mail servers to use for verification. Receiving mail servers then use the public key from DNS to decrypt the signature and make sure that the signed headers and message body were not modified in transit. I’ll describe DomainKeys here, and just understand that DKIM might implement things slightly differently.
The DomainKeys spec is available here
The DKIM spec is available here
Note that this doesn’t actually do any spam checking on its own. It simply verifies that the message came from an who it claims and wasn’t tampered with along the way. It is intended to be used for fighting phishing, and creating a reliable reputation-based services.
Originating mail server:
When a message is submitted to a mail server for signing, it uses the ‘From’, and ‘Sender’ headers, as well as the message body, in combination with the Private Key to generate a DomainKeys signature. This signature is added to the rest of the email headers and looks something like this:
DomainKey-Signature: a=rsa-sha1; s=mail; d=somedomain.com; c=simple; q=dns;
You’ll notice that the header contains several name=value pairs that describe things about the signature. Here are some important options that may be used in the Signature:
a= describes the hash algorithm used to create the key (usually rsa-sha1)
s= contains the selector (described later).
d= the domain to use for signing
q= the method for retrieving the public key (only dns is currently supported)
p= the base64 encoded signature
See section 3.3 in the DomainKeys specs for more details or Section 3.5 in the DKIM spec
After the MTA adds these headers, it forwards the message as normal to the recipient.
The receiving mail server can then perform some validation on messages that it receives which contain a DomainKey-Signature header. To do that the receiving MTA receives the message.
For every message (regardless of weather or not it has a DomainKeys-Signature header) it receives, it looks up the DomainKeys policy record for the senders domain. This is a text record with the name ‘_domainkey.DOMAINNAME.TLD’ where the ‘_domainkey’ part of that is always ‘_domainkey’. If the message does have a DomainKeys-Signature header, the -d parameter should match the domain portion of the ‘From’ header.
The policy record for a domain has these options:
Section 3.6.2 in the DomainKeys spec and 7.4 in DKIM specs
Of interest include:
o= can be either a hyphen (-) to indicate that the domain signs all email (and non-signed messages can be treated as invalid) or a tilde (~) to indicate that the domain signs some messages.
t= Set this to ‘y’ to indicate that you are testing. Receiving mail servers are not supposed to penalize you if the message does not verify and the ‘t’ flag is on.
A Policy record for ‘somedomain’ might look like this:
[root@host ~]# dig +short -ttxt _domainkey.somedomain.com
"t=y; o=~; email@example.com"
(not that the ‘dig’ application is adding quotes around the record and adding backslashes before the semicolons.) the actual record in my DNS server looks like this:
t=y; o=~; firstname.lastname@example.org
You can use the DomainKeys Policy Record Checker to verify that your policy record is in an acceptable format.
Next, for messages that have a DomainKeys-Signature header, it uses the s= flag which describes the selector to create a DNS TXT query for
<selector>._domainkey.<domain>.<tld>. It will look something like this:
[root@host ~]# dig +short -ttxt mail._domainkey.domain.com
"k=rsa; p=MHwwDQYJKoZI...a bunch of characters removed for formatting...UAfbN8pL/f6YwIDAQAB"
Of interest here are these options:
k= The key type (always ‘rsa’ for now)
g= (dkim only) used to limit the addresses that a public key can be used for. For instance to outsource some email without giving the third party permission to sign any address.
n= human-readable notes (not to be used for anything)
You can use the DomainKeys Selector Record Tester to verify that your selector is set up properly
After the receiving mail server has retrieved these records, it can decrypt the DomainKey-Signature header and verify that the message is authentic.
For end-to-end verification that everything works, there are a couple of testers available at Skylist (for DKIM and DomainKeys) and Deliverability.com
A few email auto-replies are also available the DomainKeys page