Versenden von E-Mails mit DKIM mit .NET und MailKit
Wer heute automatisierte E-Mails versenden will, die nicht unbedingt in jedem Spam-Filter hängen bleiben sollen, der kommt kaum an DKIM vorbei.
DKIM - DomainKeys Identified Mail - ist ein Mechanismus zur Validierung von E-Mail-Absendern; primär über DNS Einträge.
Mit Hilfe eine DNS-TXT Eintrags wird vereinfacht gesagt ein Public-Key bekannt gegeben, den der E-Mail-Empfänger verwenden kann, um den Ursprung einer E-Mail validieren zu können - wozu die E-Mail Nachricht mit Hilfe eines Private Keys eine Signatur erhält.
Mehr über die prinzipelle Funktionsweise auf Wikipedia.
Generierung der Keys
Für DKIM können selbst erstellte Schlüsselpaare verwendet werden; dazu ist kein gekauftes Zertifikat notwendig.
Dank des OpenSSL-Tools ist dies mit zwei einfachen Befehlen umsetzbar:
Erstellung des privaten Schlüssels für das Signieren der E-Mail:
openssl genrsa -out beispiel.dkim.private 1024
Mit Hilfe des privaten Schlüssels kann nun ein öffentlicher Schlüssel erzeugt werden, der im DNS bekannt gegeben wird:
openssl rsa -in beispiel.dkim.private -out beispiel.dkim.public -pubout -outform PEM
Setzen des DNS Eintrags
Mit Hilfe des DNS-Eintrags validiert der E-Mail Empfänger die Signatur.
Der Inhalte des öffentlichen Schlüssels - ohne den Header, Footer oder Leerzeichen - kommt nun in folgendes DNS-Format:
v=DKIM1;k=rsa;t=y;p=Undzwn/n2ndsk......UZnbhudnz=;
Der Inhalt de p-Parameters muss der Inhalt aus dem öffentlichen Schlüssel entsprechen.
Der Name des TXT-Eintrags lautet beispielselektor2021._domainkey
wobei beispielselektor2021
ein Platzhalter ist, denn einer Domain können prinzipiell viele Domain Keys hinterlegt werden, sodass man jeden E-Mail Ausgang mit eigenen Schlüsseln konfigurieren kann. Der Selektor wird dabei beim Erstellen der Signatur mit in die E-Mail gepackt, sodass der E-Mail Empfänger weiß, welcher der zugehörige Eintrag ist.
Da jeder Domain-Anbieter eine andere Oberfläche mit sich bringt, wie ein TXT-Eintrag zu setzen ist, überspringe ich diesen Part.
Signieren der E-Mail mit MimeKit
Anders als mit .NET Boardmitteln, die keine direkte Möglichkeit bieten E-Mails mit DKIM zu signrieren, bietet das MimeKit entsprechende Unterstützung an.
Hierzu erzeugen wir mit der Partnerbibliothek MimeKit entsprechend eine E-Mail, setzen Absender und Empfänger sowie den Inhalt - und signieren am Schluss die E-Mail.
MimeMessage emailMessage = new(.....);
{
// set from
// set to
// set body
// set attachments
}
string pk = $"-----BEGIN RSA PRIVATE KEY-----\r\n{PrivateKeyString}\r\n-----END RSA PRIVATE KEY-----";
MemoryStream stream = new(Encoding.UTF8.GetBytes(pk));
{
stream.Position = 0;
}
// Erzeugung des Signers mit dem privaten Schlüssel; auch das Lesen einer Datei selbst ist als Übergabeparameter möglich!
DkimSigner dkimSigner = new DkimSigner(stream, "meinedomain.de", "beispielselektor2021");
// Welche Elemente der Mail sollen mit signiert werden?
HeaderId[] dkimSignHeaders = { HeaderId.From, HeaderId.To, HeaderId.Subject, HeaderId.Date, HeaderId.MessageId };
// Signieren der Mails
emailMessage.Body.Prepare(EncodingConstraint.SevenBit);
dkimSigner.Sign(FormatOptions.Default, emailMessage, dkimSignHeaders);
// Versenden
smtpClient.SendAsync(emailMessage....);
Das Signieren muss unbedingt am Schluss passieren, da ansonsten entsprechend sich die Inhalte nach der Signatur verändern und die Signatur ungültig wird. Ebenso ist notwendig, dass der Inhalte der Mail - also der Body - durch Prepare()
das entsprechende Encoding erhält; fehlt das, ist auch hier die Signatur am Ende ungültig.
Testen und Validieren der Signatur
Es gibt viele verschiedene Tools, die eine DKIM-Validierung anbieten; wirklich empfehlen kann ich aber im Endeffekt nur zwei Tools:
- Microsoft Outlook kann mit Hilfe des MHA-Addins entsprechende Informationen zu E-Mail Headern anzeigen.
- Mit Hilfe des Services https://dkimvalidator.com/ kann eine Test-E-Mail versendet werden, die entsprechend analyisiert wird
Outlook MHA:
Der Message Header Inilizer sollte - bei einer korrekten Implementierung von DKIM - folgenden Header-Ausschnitt anzeigen
DKIM-Signature: v=1; a=rsa-sha256; d=meinedomain.de; s=1und1; c=simple/simple;
t=1610929057; h=from:to:subject:date:message-id;
bh=IR0r/61rl3Xcv7EAYlNJVNAgqOpheEklTFjbFnFyGIU=;
b=OXRyC6ZwDAWTVhKzmHiI0gURspIbw5xNCyzYo5D45iqcy1F5l9UljlGMU38lt4Sy3JLlAn5k2PX
/DIFDnmRDaBnw+hVHdlwDBC9FWJawZyyVaYzyIpQ3AG+52g8LuQ+SCqIHogFW/IkYnsNajOSVhbRZ
G/qQ2wnZoM0tWpDu2RA=
1705
Dies bestägtigt, dass Signatur über das MimeKit erfolgt ist.
Ein weitrer Header-Eintrag bestätigt darüber hinaus, ob die Signatur selbst gültig ist. Dies ist der Authentication-Results
Header, in dem allgemeine Validierungen erfolgen; darunter auch entsprechend der DKIM-Eintrag dkim=test (signature was verified)
was uns die Information gibt, dass die Validierung erfolgt und gültig ist.
Authentication-Results: spf=pass (sender IP is xxx)
smtp.mailfrom=meinedomain.de; meinedomain.com; dkim=test (signature was
verified) header.d=meinedomain.de;zieldomain.com; dmarc=pass action=none
header.from=meinedomain.de;compauth=pass reason=100
Fehlersuche
Sollte die die Validierung nicht erfolgreich sein, so ist eine Validierung gar nicht so einfach.
Zunächst muss natürlich nachgeschaut werden, ob der entsprechende Header teil der E-Mail ist; wenn nicht, dann liegt der Fehler auf der Code-Seite. Ansonsten, sollte die Validierung vorhanden aber ungültig sein, liegt es meist am Key-Pair.
Lohnt sich DKIM?
Ganz klar: Ja.
DKIM ist relevant für das Spam-Scoring; und entsprechend sollten automatisierte E-Mails - zB. von Plattformen und Webseiten - ein entsprechend hohes Scoring haben, sodass eben solche Mails nicht im Spam-Filter landen und die Kommunikation mit dem Anwender erschweren.
Dank dem MimeKit und dem MailKit ist das - zumindest aus .NET sicht für SMTP - jedoch kein Problem mehr.