DomainKeys and DKIM

Posted in How Did I Do That?

Briefly, DKIM involves using signing an email header with a private key, that is authenticated using a public key (made available as a DNS record). Make certain the key is at least 1024 bit

  1. Smartermail will generate keys for you for use only with Smartermail. Login to Smartermail, go to configuration, certificates. Generate a key with a unique identifier, then copy that key to a DNS entry:

    TXT (Text Data Record)
    HWMiden._domainKey
    p=MIGfMA0GCSqGSIb...tPpQZ5xiq3wIDAQAB
  2. Multiple identifiers are good. For php, create another one. The easy way to do this is to complete the form right here.

It is worth noting the various settings of the domainkey:

DKIM TXT RR Format - Text

The text part of the DKIM TXT RR can contain a number of semi-colon (;) separated tag=value fields (defined in RFC 4871 Section 3.6.1). The following section documents the allowed tags and values (a number of examples are provided to show scenario specific RR values).

Note: DKIM uses a tag=value notation to define fields in both the DKIM-Signature header and the DNS TXT RR text field. Somewhat confusingly, in a number of cases the tag name part, such as v= or s=, will take the same value for both the DKIM-Signature mail header and the DNS RR. In some case the meaning will be the same but the valid values may be different, in other cases the meaning of the tag is different for each entity. Readers are advised to ensure they consult the correct section of the specification. Specifically for DKIM-Signature mail header tag=value pairs use RFC 4871 Section 3.5 (updated by RFC 5672) and for DNS TXT RR tag=value pairs use RFC 4871 Section 3.6.1.

v= (version)
Optional. Defines the DKIM version number and may only (at this time) take the (defaulted) value DKIM1. While it may be safely omitted our advice is to include it.
v=DKIM1;
g= (granularity)
Optional. Granularity defines the user (local) part of the email (everything to the left hand side of the @) to which this DKIM TXT RR applies. A single wild card (*) value may be used anywhere in the field. Defaults to g=*(all user - local - part addresses match). This value (after any wild card processing) must exactly match the mail From: user (local) part. However, assuming you are not doing anything too fancy (good luck if you are) it may be safely omitted.
# single email address form
g=joe;
# partial wild card form 
g=*-maillist;
# default form - everything
g=*;
h= (hash algorithm)
Optional. Defines one or more colon (:) separated hash (digest) algorithms that will be used for the purpose of creating digital signatures (in conjunction with k= below) covering either or both of the defined mail headers or the mail body (including, optionally, MIME attachments). Allowable values are from the set sha1 and sha256. Default is h=* (all). Since all implementations of DKIM are mandated to support both sha1 and sha256 hash (digest) algorithms it may be safely omitted.
h=sha1:sha256;
h=*;
k= (key type)
Optional. Defines the public key algorithm being used. Defaults to k=rsa. Since rsa is the only algorithm currently supported it may be safely omitted.
k=rsa;
n= (notes)
Optional. Defines human readable text that may be used by validating receiver administrators. Unless this imparts significant, perhaps world-stopping, knowledge - such as a contact phone number or email address - it may be safely omitted.
n=We are really, really trustworthy (snigger, snigger);
p= (public key material)
Defines the public key (in base64 text format) for the algorithm defined by the k= tag whose private key was used to digitally sign user defined parts of the mail item. The data for the public key may be created by openssl using the following command sequence (taken from RFC 4871 Appendix C and reproduced here only for convenience):
# Create the RSA public private key pair
# in dkim.private with a key length of 1024 bits
openssl genrsa -out dkim.private 1024

openssl rsa -in dkim.private -out dkim.public -pubout -outform PEM
# extracts the public key (in base 64 format to file dkim.public
# in PEM (Privacy Enhanced Mail) format which looks like this:
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDwIRP/UC3SBsEmGqZ9ZJW3/DkM
oGeLnQg1fWn7/zYtIxN2SnFCjxOCKG9v3b4jYfcTNh5ijSsq631uBItLa7od+v/R
tdC2UzJ1lWT947qR+Rcac2gbto/NMqJ0fzfVjH4OuKhitdY9tf6mcwGjaNBcWToI
MmPSPDdQPNUYckcQ2QIDAQAB
-----END PUBLIC KEY-----

Remove the lines beginning with "-" and edit the remaining text in any of the following formats (most key material replaces with ' ... ' for brevity):

; single line format
name._domainkey IN TXT "v=DKIM1;p=MIGfMA0G ... cQ2QIDAQAB"

; multi-line format
name._domainkey IN TXT ("v=DKIM1"
                         "p=MIGfMA0G ... "
                         "oGeLnQg ... "
                         "tdC2UzJ1lW ... "
                         "MmPSPDdQPNUYckcQ2QIDAQAB")

See TXT RR for additional information on layout and formatting of text.

If a key is to be revoked (declared invalid) then setting the p= tag to a null value will achieve this:

p=;
s= (service type)
Optional. Defines the service type to which DKIM is applied. At this time the only valid value is email but clearly the designers had their sights set on greater goals. The default is s=* (all). Since email is, currently, the only DKIM supported service it may be safely omitted.
s=email;
s=*;
t= (flags)
Optional. Defaults to no flags set. A colon (:) separated list of flags to be used by the validator. Two flags are currently defined:
y Indicates test mode. If set it may (hopefully does) generate additional diagnostic messages from the validating receiver, but still permits the validator to treat the mail normally, that is, a validation failure must still be treated as a failure - no validation leniency is implied by setting this flag.
s If defined this flag indicates that this key is not valid for subdomains of the domain name (defined in the d= tag of the DKIM-Signature). To inhibit subdomain signing behavior in OpenDKIM you must set SubDomains No.
t=y:s;

Using DKIM ffrom PHPmailer should be fairly simple. Just add the following:

require_once(dirname(__FILE__)."/libraries/phpmailer/phpmailer.php");

$mail = new PHPMailer(); // defaults to using php "mail()"
$mail->DKIM_domain = 'domainbooth.com';
$mail->DKIM_identity = This email address is being protected from spambots. You need JavaScript enabled to view it.';
$mail->DKIM_private = 'G:\\Domains\\domainbooth.com\\private\\htkeyprivatefilename';
$mail->DKIM_selector = 'HWMiden'; //see above for identifier
$mail->DKIM_passphrase = 'thisisapasswordusedwhencreatingkey';

BUT there is a problem with PHPmailer not forming a proper hash. Instead, you must use IsSMTP:

require_once(dirname(__FILE__)."/libraries/phpmailer/phpmailer.php");

$mail = new PHPMailer(); // defaults to using php "mail()"

// it would probably process fast without using SMTP, but the DKIM hash is not including the To field, thereby creating a bad hash.
$mail->IsSMTP();
$mail->SMTPAuth   = true;                  // enable SMTP authentication
$mail->Host       = "mail.domainbooth.com"; // sets the SMTP server
$mail->Port       = 25;                    // set the SMTP port for the GMAIL server
$mail->Username   = "This email address is being protected from spambots. You need JavaScript enabled to view it."; // SMTP account username
$mail->Password   = "very-secret-password-goes-here";        // SMTP account password

$mail->DKIM_domain = 'domainbooth.com';
$mail->DKIM_identity = This email address is being protected from spambots. You need JavaScript enabled to view it.';
$mail->DKIM_private = 'G:\\Domains\\domainbooth.com\\private\\htkeyprivatefilename';
$mail->DKIM_selector = 'HWMiden'; //see above for identifier
$mail->DKIM_passphrase = 'thisisapasswordusedwhencreatingkey';




This all sounds so simple, I cannot image forgetting. But it is quite helpful to help insure emails are actually received.