Hello! How to get the Dkim key from a database field. For example, I have a object called from_domain, where it has a attribute called private key. So, Can I sign it in this way?
privkey = "-----BEGIN RSA PRIVATE KEY-----\ %s\ -----END RSA PRIVATE KEY----" % from_domain.private_key sig = dkim.sign(message=message.as_string(), selector=str(from_domain.selector), domain=from_domain.domain_name, privkey=privkey, include_headers=headers, canonicalize=('relaxed','relaxed'),) message.add_header('DKIM-Signature', sig.lstrip("DKIM-Signature: "))
If you have run into bytes / string trouble trying to run this script with Python 3 (tested with 3.6.8), please check the Stackoverflow answer here: https://stackoverflow.com/a/46984906/12334678
This helped me get my script working properly.
For Python 3 DKIM sign function must be like this:
sig = dkim.sign( message=msg.as_bytes(), selector=bytes(dkim_selector,'UTF8'), domain=bytes(sender_domain,'UTF8'), privkey=bytes(dkim_private_key,'UTF8'), include_headers=headers )
Can confirm this change is needed for Python3, I also had to change the line msg["DKIM-Signature"] = sig.lstrip("DKIM-Signature: ") to msg["DKIM-Signature"] = sig[len("DKIM-Signature: "):].decode() The stackoverflow listed above showed the first part of that, but without the .decode() to change it from byte to string I kept getting the error AttributeError: 'bytes' object has no attribute 'encode' on the sendmail function.
Are you able to provide an example of "dkim_selector" (DKIM selector), what should I be using for this?
Install the public key (.pub
) as a DNS TXT record, where the record name ("selector") is 20180605._domainkey
and the value body is:
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDcplYPRsqIFwXuggtH2XgQDMX+e+6sGnWdV8ld/FR9zgRAxB+DeiCEVooVvYt2JRZUEokgDFvys82Q+JTbN4qHNz19bdcBGrnTsnIFaQYpgeQYmPLdDtcWRKzTYMRNCnRmmEXyGv7WIDcaTapIq9NFgLmy1QT7ZTxuNjQtDB/2LwIDAQAB;
You may choose any selector, I happen to like to use YearMonthDay. Additionally you will substitute your public key in place of mine. Put each the line of the public key on a single line in the TXT record.
Thanks a lot! I edited the script in this article and added below line:
send_email("I-Do-Exist-victim@gmail.com", "I-do-not-exist@attacker-domain.com", "Test Email Subject", "Some text msg", "", "localhost", "/etc/dkim/attacker-domain.com.20211126.pem", "20211126._domainkey")
addresses and domain are in reality different...I have purchased a domain and added a TXT record:
- name: 20211126._domainkey
- value: v=DKIM1; k=rsa; p=MyPublicKey;
I generated the public and private key as per this article steps (I do have a certificate for a site on this same attacker-domain.com but I think that's irrelevant, so didn't use keys from that cert).
however, i get the below response when checking /var/log/mail.log
(I modified the addresses in the output):
Nov 30 10:50:00 Ubuntu postfix/postfix-script[2928]: starting the Postfix mail system Nov 30 10:50:00 Ubuntu postfix/master[2930]: daemon started -- version 3.4.13, configuration /etc/postfix Nov 30 10:50:11 Ubuntu postfix/postfix-script[3499]: fatal: the Postfix mail system is already running Nov 30 11:11:05 Ubuntu postfix/smtpd[12030]: connect from localhost[127.0.0.1] Nov 30 11:11:05 Ubuntu postfix/smtpd[12030]: AD0EA4205D: client=localhost[127.0.0.1] Nov 30 11:11:05 Ubuntu postfix/cleanup[12034]: AD0EA4205D: message-id=<20211130111105.AD0EA4205D@Ubuntu.xxxI removed thisxxxx> Nov 30 11:11:05 Ubuntu postfix/qmgr[2932]: AD0EA4205D: from=<I-do-not-exist@attacker-domain.com>, size=1369, nrcpt=1 (queue active) Nov 30 10:50:00 Ubuntu postfix/postfix-script[2928]: starting the Postfix mail system Nov 30 10:50:00 Ubuntu postfix/master[2930]: daemon started -- version 3.4.13, configuration /etc/postfix Nov 30 10:50:11 Ubuntu postfix/postfix-script[3499]: fatal: the Postfix mail system is already running Nov 30 11:11:05 Ubuntu postfix/smtpd[12030]: connect from localhost[127.0.0.1] Nov 30 11:11:05 Ubuntu postfix/smtpd[12030]: AD0EA4205D: client=localhost[127.0.0.1] Nov 30 11:11:05 Ubuntu postfix/cleanup[12034]: AD0EA4205D: message-id=<20211130111105.AD0EA4205D@Ubuntu.xxxI removed thisxxxx> Nov 30 11:11:05 Ubuntu postfix/qmgr[2932]: AD0EA4205D: from=<I-do-not-exist@attacker-domain.com>, size=1369, nrcpt=1 (queue active) Nov 30 11:11:05 Ubuntu postfix/smtpd[12030]: disconnect from localhost[127.0.0.1] ehlo=1 mail=1 rcpt=1 data=1 quit=1 commands=5 Nov 30 11:11:06 Ubuntu postfix/smtp[12035]: connect to gmail-smtp-in.l.google.com[2404:6800:4003:c03::1a]:25: Network is unreachable Nov 30 11:11:09 Ubuntu postfix/smtp[12035]: AD0EA4205D: to=<I-Do-Exist-victim@gmail.com>, relay=gmail-smtp-in.l.google.com[74.125.24.27]:25, delay=3.3, delays=0.01/0.01/2.2/1.1, dsn=5.7.26, status=bounced (host gmail-smtp-in.l.google.com[74.125.24.27] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. s5si23402633plp.197 - gsmtp (in reply to end of DATA command))
Hence I edited the send_mail definition to below but still got the same error:
def send_email( to_email, sender_email, subject, message_text, message_html, relay, dkim_private_key_path, dkim_selector, ):
- What am I doing wrong?
- another question: the "from" can be substituted to be anything, as long as the domain exists? for example, I-do-not-exist@facebook.com? ...am I correct?
This log line tells you what gmail claims happened:
Nov 30 11:11:09 Ubuntu postfix/smtp[12035]: AD0EA4205D: to=<I-Do-Exist-victim@gmail.com>, relay=gmail-smtp-in.l.google.com[74.125.24.27]:25, delay=3.3, delays=0.01/0.01/2.2/1.1, dsn=5.7.26, status=bounced (host gmail-smtp-in.l.google.com[74.125.24.27] said: 550-5.7.26 This message does not have authentication information or fails to 550-5.7.26 pass authentication checks. To best protect our users from spam, the 550-5.7.26 message has been blocked. Please visit 550-5.7.26 https://support.google.com/mail/answer/81126#authentication for more 550 5.7.26 information. s5si23402633plp.197 - gsmtp (in reply to end of DATA command))
Are you sending through an SMTP server that is covered by the SPF record?
Reference:
I didn't have SPF record, but I just added it as below but I still got the same error.
name: attacker-domain.com value: v=spf1 ipxxx.xxx.xxx.xxx +all
where xxx.xxx.xxx.xxx is the IP of the ubuntu machine that is executing the python script and Postfix.
you asked "Are you sending through an SMTP server..." so I guess I miss something, as all I did is installing Postfix (haven't touched its configuration other than choosing the attacker-domain.com when prompted to during installation), and bought a regular domain (didn't purchase anything related to emails) and set two TXT records for it (one for DKIM and one for SPF).
Isn't Postfix supposed to act as my SMTP server?
Thanks very much! This is a great tutorial, If anyone encounters this error:
"header_store_parse raise ValueError("Header values may not contain linefeed " from the email/policy.py on this line:
msg["DKIM-Signature"] = sig[len("DKIM-Signature:") :].decode()
you can replace the CR LF with:
sig = sig.replace(b"\r\n",b"") before assigning value to msg["DKIM-Signature"]
Thank you! I used this approach to add DKIM feature to my https://github.com/yaroslaff/testmsg project (CLI utility to make/send perfectly valid test messages).
When working on it, I got ValueError
exception "Header values may not contain linefeed or carriage return characters".
I use message as instance of email.message.EmailMessage
class and it does not like newlines in headers (it wrap headers itself). Solution was to add linesep=b''
to dkim.sign()
:
sig = dkim.sign(
message=msg.as_bytes(),
selector=str(args.selector).encode(),
domain=domainname.encode(),
privkey=dkim_private_key.encode(),
include_headers=headers,
linesep=b''
)
If someone wants to use testmsg sources as working example (and maybe borrow code) you are welcome!
Thanks for posting this! You're right; there's not much in the way of examples out there.
Unfortunately, it's failing for me:
Traceback (most recent call last): File "/usr/lib/python3/dist-packages/dkim/crypto.py", line 140, in parse_private_key pka = asn1_parse(ASN1_RSAPrivateKey, data) File "/usr/lib/python3/dist-packages/dkim/asn1.py", line 91, in asn1_parse raise ASN1FormatError( dkim.asn1.ASN1FormatError: Unexpected tag (got 6f, expecting 30)
I'm using your code exactly, and calling your function like this:
if __name__ == '__main__': send_email( to_email='strnbrg59@gmail.com', sender_email='strnbrg59@strnbrg59.com', subject='dkim mail test', message_text='This is a test of dkim-signed mail', message_html='This is the html version', dkim_private_key_path='/home/strnbrg59/.ssh/id_rsa', dkim_selector='20240212._domainkey')
where 20240212._domainkey is the name I've assigned to the TXT record at godaddy.com.
Any thoughts? I am 100% sure my private/public key situation is good on this server.
Thanks for posting this! As you say, there's not much in the way of examples out there.
Unfortunately, it's not working for me:
Traceback (most recent call last): File "/usr/lib/python3/dist-packages/dkim/crypto.py", line 140, in parse_private_key pka = asn1_parse(ASN1_RSAPrivateKey, data) File "/usr/lib/python3/dist-packages/dkim/asn1.py", line 91, in asn1_parse raise ASN1FormatError( dkim.asn1.ASN1FormatError: Unexpected tag (got 6f, expecting 30)
I'm using your code exactly, and calling it like so:
if __name__ == '__main__': send_email( to_email='strnbrg59@gmail.com', sender_email='strnbrg59@strnbrg59.com', subject='dkim mail test', message_text='This is a test of dkim-signed mail', message_html='This is the html version', dkim_private_key_path='/home/strnbrg59/.ssh/id_rsa', dkim_selector='20240212._domainkey')
where 20240212._domainkey is what I've named my DNS TXT record at godaddy.com. I'm 100% sure my private/public key situation on this server is good, and I've verified that I am supplying the correct path to my private key (which is a file that begins with "-----BEGIN OPENSSH PRIVATE KEY-----" and ends with "-----END OPENSSH PRIVATE KEY-----").