#!/usr/bin/env perl
#
# SPDX-FileCopyrightText: 2026 Graeme Walker <graeme_walker@users.sourceforge.net>
# SPDX-License-Identifier: FSFAP
#
# Copyright (c) 2026 Graeme Walker <graeme_walker@users.sourceforge.net>
#
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
# ===
#
# emailrelay-dkim-signer.pl
#
# An example E-MailRelay filter script for DKIM signing.
#
# To generate a key pair:
# $ openssl genrsa -out example.com.pk 2048
# $ openssl rsa -in example.com.pk -pubout -out example.com.pub
#
# Serve up the public key via DNS, eg:
# $ grep -v PUBLIC example.com.pub | tr -d '\n'
# upload default._domainkey TXT "p=Q8AMIIB...."
# $ nslookup -query=TXT default._domainkey.example.com
# text = "p=Q8AMIIB...."
#
# Test with spamassassin:
# $ c=`emailrelay-submit -v -s \`pwd\` -N -C DQo= -C aGVsbG8sIHdvcmxkIQ== -d -F -t -f me@example.com you@example.com`
# $ emailrelay-dkim-signer.pl -f example.com.pk $c
# $ spamassassin --debug=dkim --test-mode < $c
#
# Requires debian package 'libmail-dkim-perl'.
#
use strict ;
use IO::File ;
use File::Copy ;
use Mail::DKIM::Signer ;
use Mail::DKIM::TextWrap ;
$SIG{__DIE__} = sub { (my $e = join(" ",@_)) =~ s/\n/ /g ; print "<<error: $e>>\n" ; exit 99 } ;
# configuration
my $cfg_domain = "example.com" ;
my $cfg_pk = "/etc/dkim/private/example.com.pk" ;
# parse the command-line
if( $ARGV[0] eq "-f" ) { shift @ARGV ; $cfg_pk = shift @ARGV }
my $content = $ARGV[0] or die "usage error\n" ;
# calculate the signature
my $fh = new IO::File( $content ) or die "cannot open content file\n" ;
my $dkim = new Mail::DKIM::Signer(
Algorithm => 'rsa-sha1' ,
Method => 'relaxed' ,
Domain => $cfg_domain ,
Selector => 'default' , # => default._domainkey.example.com
KeyFile => $cfg_pk ,
Headers => '' , # 'x-header:x-header2'
);
$dkim->load( $fh ) ; # includes CLOSE()
$fh->close() or die ;
my $signature = $dkim->signature->as_string() ;
# edit the content file
$fh = new IO::File( $content.".tmp" , "w" ) or die ;
print $fh $signature , "\r\n" ;
$fh->flush() ;
File::Copy::copy( $content , $fh ) or die ;
$fh->close() or die ;
File::Copy::move( $content.".tmp" , $content ) or die ;
exit 0 ;