Feature Proposal: Add code to sign emails with DKIM

Motivation

After the last round of Google's email rejections due to PTR and SPF issues, they also recommend providing DKIM signatures. Currently not required, but I didn't want to lose the test code I created while investigating it.

Description and Documentation

Use CPAN:Mail::DKIM::Signer to add a signature to an email. It needs a few configuration settings for the following:
                Algorithm => "rsa-sha256",
                Method => "relaxed",
                Domain => "sender.domain.com",
                Selector => "foswiki",
                KeyFile => "private.key",
It's then a matter of sending the output of the email through the signing function, and adding the generated headers to the Email object.

The KeyFile "private.key" is generated using openssl. it is then used to generate the public key, which has to be added as a txt record to the DNS server. The below code (Found on the internet, and now I cannot find it again), creates the keys and prints an example TXT record.

  openssl genrsa -out private.key 1024
  openssl rsa -in private.key -out mail.key.pem -pubout -outform PEM
  perl -e '@l = <>; shift @l; pop @l; chomp @l; print qq(<selector>i._domainkey.<domain>. IN TXT  "k=rsa; t=y; p=), join("", @l), qq("\n)' < mail.key.pem

Examples

Impact

%WHATDOESITAFFECT%
edit

Implementation

So far this is only and experiment. It works but is limited. Not sure if it's bugs in Mail::DKIM.
  • Registration messages are signed and verify okay
  • The bin/configure [Test Email] button crashes. In looking at a dump of the DKIM object, it appears that the multipart structure is causing the key to be mis-located in the structure.
diff --git a/core/lib/Foswiki/Net.pm b/core/lib/Foswiki/Net.pm
index d7c426f..94c647f 100644
--- a/core/lib/Foswiki/Net.pm
+++ b/core/lib/Foswiki/Net.pm
@@ -754,10 +754,58 @@ sub _fixEmail {
     return $email;
 }
 
+sub _signDKIM {
+    my $this  = shift;
+    my $email = shift;
+
+    require Mail::DKIM::Signer;
+    require Mail::DKIM::TextWrap;    #recommended
+
+    # create a signer object
+    my $dkim = Mail::DKIM::Signer->new(
+        Algorithm => "rsa-sha256",
+        Method    => "relaxed",
+        Domain    => "foswiki.fenachrone.com",
+        Selector  => "foswiki",
+        KeyFile   => "/home/gac/perl-code/private.key",
+    );
+
+    my $raw_data = $email->as_string;
+
+    # SMELL: DKIM::Signer requires that all lines, headers and body
+    # terminate with DOS style line endings.  The Email::MIME object
+    # seems to use mixture of \n for Headers and \r\n for body.
+    # So this unifies the line endings
+    $raw_data =~ s/\r\n/\n/sg;
+    $raw_data =~ s/\n/\r\n/sg;
+
+    # Input the email into the Signer.
+    $dkim->PRINT($raw_data);
+
+    $this->_logMailError( 'debug', "====== RAW DATA =====" );
+    $this->_logMailError( 'debug', $raw_data );
+    $this->_logMailError( 'debug', "====== DKIM OBJECT ======" );
+    $this->_logMailError( 'debug', Data::Dumper::Dumper( \$dkim ) );
+    $this->_logMailError( 'debug', "====== EMAIL OBJECT ======" );
+    $this->_logMailError( 'debug', Data::Dumper::Dumper( \$email ) );
+
+    $dkim->CLOSE;    #Process the message, and generate the signature
+
+   # The header MUST appear before any headers used in generating the signature.
+   # so the $email->header_set method cannot be used. Return the text header
+   # and the caller will prepend it to the message.
+
+    my $sig = $dkim->signature;
+    return $sig->as_string();
+
+}
+
 # =======================================
 sub _sendEmailBySendmail {
     my ( $this, $email ) = @_;
 
+    my $sigDKIM = $this->_signDKIM($email); 
+
     # With feedback, unsaved values are tainted.
     # We don't have special priveleges (or shouldn't), and
     # MailProgram allows specifying an arbitrary command - e.g. rm.
@@ -777,7 +825,7 @@ sub _sendEmailBySendmail {
     my $MAIL;
     open( $MAIL, '|-:encoding(utf-8)', $mailer )
       || $this->_logMailError( 'die', "Can't send mail using $mailer" );
-    print $MAIL $email->as_string;
+    print $MAIL $sigDKIM . "\r\n" . $email->as_string;
     close($MAIL);
 
     # If you see 67 (17152) (== EX_NOUSER), then the mail is probably
@@ -803,6 +851,7 @@ sub _sendEmailByNetSMTP {
     my ( $this, $email ) = @_;
 
     my $debug = $Foswiki::cfg{SMTP}{Debug} || 0;
+    my $sigDKIM = $this->_signDKIM($email);
 
     my $from = '';
     my @to   = ();
@@ -937,7 +986,7 @@ sub _sendEmailByNetSMTP {
     $ok &&= $smtp->mail($from);
     $ok &&= $smtp->to( @to, { SkipBad => 1 } );
 
-    my $raw_email = $email->as_string;
+    my $raw_email = $sigDKIM . "\r\n" . $email->as_string;
 
     if ( $ok && $debug ) {
         my $dbg = $smtp->debug(0);

-- Contributors: GeorgeClark - 20 Dec 2015

Discussion

 
Topic revision: r4 - 13 Feb 2016, GeorgeClark
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. See Copyright Statement. Creative Commons License    Legal Imprint    Privacy Policy