Seitenanfang

Setting up DKIM email signatures with Perl

DKIM may be called as PGP successor: PGP has been used by many people for signing their emails at a time when mostly technical related people had been using the internet. Today, few people still use PGP to sign their emails, but email servers took over this part using a technology called DKIM.

dkim_signed_email.pngPGP proved that the signing person's identity, but DKIM only ensures, that the email has been send through a mailserver which is authorized for this domain, but both sign emails and both are commonly used nowadays. They're more like distant relatives than siblings and PGP isn't dead, but in the end both of them sign emails.

DKIM may be set up within a mailserver - if it's under your control - but also added by an application sending emails. Actually, it takes only 3 simple steps to sign your mails:

1. Create a pair of keys

OpenSSL is included with most default Linux installations and could be easily installed using the distributions package manager. A new private key usable for adding DKIM signatures could be easily created:

sebastian@pc:~$ openssl genrsa -out dkim.key 1024
Generating RSA private key, 1024 bit long modulus
.....................++++++
...++++++
e is 65537 (0x10001)

The new private key file dkim.key should be kept strictly secret and never published on the internet. It's required to add the signature to your mails, but the recipients (or receiving mailservers or spam filters) require a public key to validate the signature. The public key will be build out of the private key:

sebastian@pc:~$ openssl rsa -in dkim.key -out dkim.pub -pubout -outform PEM
writing RSA key

The two key files should look like this:

dkim.key:

-----BEGIN RSA PRIVATE KEY-----
MIICXgIBAAKBgQDSarYO5FDA1L9XUhI8XESATRXrnYbOP8wXkJ9jC+UR909UCiQd
37m8Nga/fxZko6hcqIMiR41oy1ozmKLeQugwT+JeG2AgIhP8dVcf9m2MXKepoPqZ
3LF+G28+tYamBYKmZOzpqgMw96/VVS9aNzhTDFYjBp59lvEPgIrzaB4ItwIDAQAB
AoGBAIAxIP+Lm6MPOg9B4fhCIj2kOQGVtAY9meKVanVWTtWWPQ8vKnfE0IxYwd2y
2HqxtiXVSCb/PqikkyyyBi+wxXFmqxwSrxgGPj+5pSCUdJzSX6G0VKbkomDruESm
AZGcvaJVnFYsAOrAmmIwuJzyq5jmoR2/epSXgxzkJvYbWVrJAkEA7Vvs2wt1O6MR
tlc8f+/MoFR/jttLHrp6x8kTB4g5sxtrAQjsraBzY9To3pnZkMj6B4kicgdGTBuE
8iK4EcELgwJBAOLxHZLQ8ox7sFezm2ksQSSiSMOLa5YiN8e+nBM1Ivn4sz+8st7v
qjX8q3eV4gkgdFmysVCuqB5R0RBmIIjSA70CQQCI/jJRJmbOOcriQ60+ZefjbYGF
sRnFLXnpzyq1gTFUJ3H10fHQMDgJGXwh5ggFIYn1SXfg+kPfb06m9dTpIlMDAkEA
mzd+mqP+wMb4XYOij8SM2ZmFWvlIpG7S5/MenKSDP6UyNC3DyWbya+pc/HWvG77u
1Uq6rAxJQDOFYkn3hpzdoQJALVWb/goGMuV5B0tpsS32k6ZaOlD2yGh8SO8X8jVF
MzylBe6dPH8TF/m/OMbgrml36JFgVH3BEyU+QVkb/QMAvg==
-----END RSA PRIVATE KEY-----

dkim.pub:

-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSarYO5FDA1L9XUhI8XESATRXr
nYbOP8wXkJ9jC+UR909UCiQd37m8Nga/fxZko6hcqIMiR41oy1ozmKLeQugwT+Je
G2AgIhP8dVcf9m2MXKepoPqZ3LF+G28+tYamBYKmZOzpqgMw96/VVS9aNzhTDFYj
Bp59lvEPgIrzaB4ItwIDAQAB
-----END PUBLIC KEY-----

Remember: Never publish your private key. I published this sample, because it has been created solely for this blog post and won't ever be used anywhere.

You might want to backup a copy of the private key file at a secret place as it's required for signing mails and can't be recovered once it's lost.

2. Create the DNS TXT records

The receiver needs your public key to verify the signature. Sending it with the email would be unsafe, because anybody would be able to send any public key and DKIM wouldn't proof anything. But there is something easily accessible for everyone which is clearly under control of the domain owner: The domain's name server.

You need to be able to add two additional entries to your domain nameserver zone:

_domainkey IN TXT "o=~;"

dkim._domainkey IN TXT "k=rsa\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDSarYO5FDA1L9XUhI8XESATRXrnYbOP8wXkJ9jC+UR909UCiQd37m8Nga/fxZko6hcqIMiR41oy1ozmKLeQugwT+JeG2AgIhP8dVcf9m2MXKepoPqZ3LF+G28+tYamBYKmZOzpqgMw96/VVS9aNzhTDFYjBp59lvEPgIrzaB4ItwIDAQAB\;"

The second entry has two configurable parts: The selector which is in front of the ._domainkey suffix. I used dkim for this sample, but anything containing a-z, 0-9 and - is ok. Each domain may have an (nearly) unlimited number of different selectors and each selector may publish it's own public key.

The second part is shown between the p= and the final \;". It's the public key from the dkim.pub file created in step one without any spaces and line breaks. It's build by simply removing the header -----BEGIN PUBLIC KEY-----, footer -----END PUBLIC KEY-----, line breaks, spaces from the dkim.pub file.

Some more options are allowed by the DKIM specification, see RFC 6376 section 3.6.1 for details.

3. Sign a mail

CPAN has Mail::DKIM::Signer which does most of the work. The installation should be straightforward using any CPAN client, but many Linux distributions also have packages for the module.

First, load the module and create a signer object:

use Mail::DKIM::Signer;
my $dkim = Mail::DKIM::Signer->new(
Algorithm => "rsa-sha1",
Method => "relaxed",
Domain => $fromdomain, # Part following the @ within the from-address sending the email
Selector => "dkim", # Selector as configured in DNS
KeyFile => "/path/to/your/private/dkim.key",
);

If you're sending your mails from my@domain.tld, set $fromdomain to domain.tld (or add a static value using single quotes if you want). The Selector needs to be exactly the same value as in step 2 and the KeyFile must contain the name of your private key file including the full path. Don't put this file into any web-accessible directory or at least deny every access to this file using a .htaccess file rule.

Next, create your email including the header lines which will not change during transmission. A good choice would be

  • From (required by DKIM specification)
  • To
  • Subject
  • Date
  • Reply-To

Do not include header files which will grow or change while the email is traveling through the internet, like Received. Including them will invalidate your signature! Now push everything into the DKIM signer:

eval {
$dkim->PRINT($header . "\r\n" . $body);
$dkim->CLOSE;
};
print "DKIM Signature failed: $@" if $@;

You may use as much ->PRINT calls as you like but only one ->CLOSE which tells the signer that the message is complete.

Finally get the signature header line and append it to your header:

$header .= $dkim->signature->as_string."\r\n";

Now add any more header fields that shouldn't be signed and simply send the message.

Don't forget to send a test message to yourself first. You should see your signature in the email header and there are many plugins for many email clients to validate the DKIM signature, like DKIM Verifier for Thunderbird

 

3 Kommentare. Schreib was dazu

  1. David Dachdecker

    Thank you very much for the instruduction.
    Also nice is the code which is given by you :)
    I will visit your blog more often now.

    greetings David

  2. And don't forget to test your results. Try these:

    http://domainkeys.sourceforge.net/

    See particularly the posting addresses listed under the heading: 'Interoperability Testing'

    http://domainkeys.sourceforge.net/cgi-bin/check_policy?domain=example.com&Submit=Submit
    (with your actual domain name of course)

    • Sebastian

      Great resource! Thank you!

Schreib was dazu

Die folgenden HTML-Tags sind erlaubt:<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>