def modify(self, mailfrom, recipients, data): import dkim if not isinstance(data, basestring): data = data.as_string() dkimhdr = dkim.sign(data, self.selector, self.domain, self.key, logger=self.logger, **self.dkimargs) return (mailfrom, recipients, dkimhdr + data)
def _send_mail_via_mime(from_, to, mime): import smtplib from rogerthat.settings import get_server_settings settings = get_server_settings() mime_string = mime.as_string() logging.info("mime_string type: %s", type(mime_string)) if DEBUG: logging.warn("Not sending real email via mime\n%s", mime_string) for part in mime.walk(): logging.info("part.get_content_type(): %s", part.get_content_type()) if part.get_content_type() == 'text/plain': logging.info(base64.b64decode(part.get_payload())) return if settings.dkimPrivateKey: if from_ == settings.dashboardEmail or "<%s>" % settings.dashboardEmail in from_: logging.info("Adding dkim signature") try: import dkim signature = dkim.sign(mime_string, 'dashboard.email', settings.dashboardEmail.split('@')[1], settings.dkimPrivateKey, include_headers=['To', 'From', 'Subject']) logging.info("signature type: %s", type(signature)) mime_string = signature.encode('utf-8') + mime_string except: logging.exception("Could not create dkim signature!") else: logging.info("Skipping dkim signature because '%s' != '%s'", from_, settings.dashboardEmail) mailserver = smtplib.SMTP_SSL(settings.smtpserverHost, int(settings.smtpserverPort)) mailserver.ehlo() mailserver.login(settings.smtpserverLogin, settings.smtpserverPassword) mailserver.sendmail(from_, to, mime_string) mailserver.quit()
def get_sign(self, email, sender=None): assert isinstance(email, EmailFacade) sender_address = email.from_address.address if sender is None else sender #todo: validation! if sender_address.count('@') != 1: raise Exception('Invalid from address format') user, domain = sender_address.split('@') domain_config = self._try_get_domain_config(domain) if domain_config is None: raise Exception('Unknown sender domain ' + domain) if not os.path.exists(domain_config.private_key): raise Exception('No private key file ' + domain_config.private_key) with open(name=domain_config.private_key, mode='r') as fd: key_data = fd.read() fd = StringIO() email.write_to(fd) fd.seek(0) signature = dkim.sign(message=fd.read(), selector=domain_config.selector, domain=domain_config.domain, include_headers=domain_config.headers, privkey=key_data) fd.close() return signature.replace('DKIM-Signature: ', '')
def _send(self, email_message): """A helper method that does the actual sending + DKIM signing.""" if not email_message.recipients(): return False from_email = sanitize_address(email_message.from_email, email_message.encoding) recipients = [sanitize_address(addr, email_message.encoding) for addr in email_message.recipients()] message_string = email_message.message().as_string() signature = '' if self.dkim_selector and self.dkim_domain and self.dkim_private_key: signature = dkim.sign(message_string, self.dkim_selector, self.dkim_domain, self.dkim_private_key) try: self.connection.sendmail(from_email, recipients, signature+message_string.as_bytes(linesep='\r\n')) except: if not self.fail_silently: raise return False return True
def dkim_sign(self, message): # Courtesy of http://djangosnippets.org/snippets/1995/ raw_message = message.message().as_string() if settings.DKIM_PRIVATE_KEY: included_headers = ["Content-Type", "MIME-Version", "Content-Transfer-Encoding", "Subject", "From", "To"] dkim_header = dkim.sign(raw_message, settings.DKIM_SELECTOR, settings.DKIM_DOMAIN, settings.DKIM_PRIVATE_KEY, include_headers=included_headers) raw_message = dkim_header + raw_message return raw_message
def test_verifies(self): # A message verifies after being signed. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign( self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo)) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertTrue(res)
def test_altered_body_fails(self): # An altered body fails verification. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign( self.message, b"test", b"example.com", self.key) res = dkim.verify( sig + self.message + b"foo", dnsfunc=self.dnsfunc) self.assertFalse(res)
def _dkim_sign_email(raw_email): ''' Signs the raw email according to DKIM standards and returns the resulting email (which is the original with extra signature headers). ''' sig = dkim.sign(raw_email, settings.DKIM_SELECTOR, settings.DKIM_DOMAIN, open(settings.DKIM_PRIVATE_KEY).read()) return sig + raw_email
def send(self, *args): """ Must Be Two Parameters recipient and email body """ if not args: raise SMTPServerError("Fatal error: Email Content must have the recipient and body") if len(args) == 1: raise SMTPServerError("Fatal error: Email Content must have the body") """ try: self.server.connect(self.host,self.port) except Exception,e: raise SMTPServerError(e) """ recipient=args[0] body=args[1] senddate="%s %s"%(time.strftime("%a, %d %b %Y %H:%M:%S",time.localtime()),"+0800") messageid="<%s>"%recipient mimeversion = "1.0" header="From:%s\r\nTo:%s\r\nSubject:%s\r\nMIME-Version:%s\r\nMessage-Id:%s\r\nDate:%s\r\nX-Mailer:%s\r\nContent-Type:%s"%(self.mailfrom,recipient,self.subject,mimeversion,messageid,senddate,XMailer,self.contenttype) content="%s\r\n\r\n%s"%(header,body) content == dkim.rfc822_parse(content) if self.dkim: domain = re.search(r"[^@]+$",self.mailfrom.strip()).group(0) sig = dkim.sign(content,'_dkim',domain,open(self.dkim).read(),include_headers=["From","To","Subject","Date","Message-Id","X-Mailer"],canonicalize=(dkim.Simple, dkim.Relaxed)) content = sig + content Retry = 3 while True: try: self.server.sendmail(self.sender,recipient,content) return True break except smtplib.SMTPServerDisconnected: if Retry == 0: raise SMTPServerError("Lost the Connection with the Server") return False break else: try: self.connect() Retry = Retry - 1 except: Retry = Retry - 1 except Exception,e: if Retry == 0: raise SMTPServerError(e) return False break else: Retry = Retry - 1
def test_ignores_tlsrptsvc(self): # A non-tlsrpt signed with a key record with s=tlsrpt shouldn't verify. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo)) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc6) self.assertFalse(res)
def fake_signing(self, plain_message, canonicalize=None): if canonicalize is None: canonicalize = (dkim.Relaxed, dkim.Relaxed) dkim_line = dkim.sign(plain_message, selector='example', domain='canonical.com', privkey=sample_privkey, debuglog=self._log_output, canonicalize=canonicalize) assert dkim_line[-1] == '\n' return dkim_line + plain_message
def test_add_body_length(self): sig = dkim.sign(self.message, b"test", b"example.com", self.key, length=True) msg = email.message_from_string(self.message.decode('utf-8')) self.assertIn('; l=%s' % len(msg.get_payload() + '\n'), sig.decode('utf-8')) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertTrue(res)
def test_unknown_k(self): # A error is detected if an unknown algorithm is in the k= tag. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo)) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc4) self.assertFalse(res)
def test_bad_version(self): # A error is detected if a bad version is used. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo)) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc3) self.assertFalse(res)
def test_verifies(self): # A message verifies after being signed. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo)) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertTrue(res)
def test_string_include(self): # A message can be signed when the include_headers is string for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo), include_headers=('from', )) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertTrue(res)
def test_simple_signature(self): # A message verifies after being signed with SHOULD headers for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo), include_headers=(b'from', ) + dkim.DKIM.SHOULD) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertTrue(res)
def test_double_verifies(self): # A message also containing a ed25519 signature verifies after being signed with rsa. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message3, b"test", b"football.example.com", self.key, canonicalize=(header_algo, body_algo), signature_algorithm=b'rsa-sha256') res = dkim.verify(sig + self.message3, dnsfunc=self.dnsfunc5) self.assertTrue(res)
def send_mail(**kwargs): """ Send a mail. Usage example: .. code-block:: send_mail(subject="test", email="*****@*****.**", message="TEST") If email is a list, mails are sent as Cci. :param \\**kwargs: See below :Keyword Arguments: * *subject* (``string``) -- Email subject * *email* (``string`` or ``list(string)``) -- Email Adress recipient * *message* (``string``) -- Email body """ config = current_app.config s = smtplib.SMTP(host=config["SMTP_HOST"], port=config["SMTP_PORT"]) s.starttls() s.login(config["SMTP_ADDRESS"], config["SMTP_PASSWORD"]) msg = MIMEMultipart() msg["From"] = config["SMTP_ADDRESS"] msg["Subject"] = kwargs["subject"] msg["Message-ID"] = email.utils.make_msgid(domain=config["SERVER_NAME"]) msg["Date"] = email.utils.formatdate() dest = kwargs["email"] if isinstance(dest, list): msg["Bcc"] = ",".join(dest) else: msg["To"] = dest msg.attach(MIMEText(kwargs["message"], "plain", "utf-8")) # DKIM part if config["DKIM_KEY"] != "" and config["DKIM_SELECTOR"] != "": sig = dkim.sign( message=msg.as_bytes(), selector=config["DKIM_SELECTOR"].encode(), domain=config["SMTP_ADDRESS"].split("@")[-1].encode(), privkey=config["DKIM_KEY"].encode(), include_headers=["From", "To", "Subject", "Message-ID"], ) msg["DKIM-Signature"] = sig.decode("ascii").lstrip("DKIM-Signature: ") s.send_message(msg)
def test_dkim_signature_canonicalization(self): # <https://bugs.launchpad.net/ubuntu/+source/pydkim/+bug/587783> # Relaxed-mode header signing is wrong # <https://bugs.launchpad.net/dkimpy/+bug/939128> # Simple-mode signature header verification is wrong # (should ignore FWS anywhere in signature tag: b=) sample_msg = b"""\ From: [email protected] To: [email protected] Subject: this is my test message """.replace(b'\n', b'\r\n') sample_privkey = b"""\ -----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBANmBe10IgY+u7h3enWTukkqtUD5PR52Tb/mPfjC0QJTocVBq6Za/ PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQJAYFUKsD+uMlcFu1D3YNaR EGYGXjJ6w32jYGJ/P072M3yWOq2S1dvDthI3nRT8MFjZ1wHDAYHrSpfDNJ3v2fvZ cQIhAPgRPmVYn+TGd59asiqG1SZqh+p+CRYHW7B8BsicG5t3AiEA4HYNOohlgWan 8tKgqLJgUdPFbaHZO1nDyBgvV8hvWZUCIQDDdCq6hYKuKeYUy8w3j7cgJq3ih922 2qNWwdJCfCWQbwIgTY0cBvQnNe0067WQIpj2pG7pkHZR6qqZ9SE+AjNTHX0CIQCI Mgq55Y9MCq5wqzy141rnxrJxTwK9ABo3IAFMWEov3g== -----END RSA PRIVATE KEY----- """ sample_pubkey = """\ -----BEGIN PUBLIC KEY----- MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANmBe10IgY+u7h3enWTukkqtUD5PR52T b/mPfjC0QJTocVBq6Za/PlzfV+Py92VaCak19F4WrbVTK5Gg5tW220MCAwEAAQ== -----END PUBLIC KEY----- """ for header_mode in [dkim.Relaxed, dkim.Simple]: dkim_header = dkim.sign(sample_msg, b'example', b'canonical.com', sample_privkey, canonicalize=(header_mode, dkim.Relaxed)) # Folding dkim_header affects b= tag only, since dkim.sign folds # sig_value with empty b= before hashing, and then appends the # signature. So folding dkim_header again adds FWS to # the b= tag only. This should be ignored even with # simple canonicalization. # http://tools.ietf.org/html/rfc4871#section-3.5 signed = dkim.fold(dkim_header) + sample_msg result = dkim.verify(signed,dnsfunc=self.dnsfunc, minkey=512) self.assertTrue(result) dkim_header = dkim.fold(dkim_header) # use a tab for last fold to test tab in FWS bug pos = dkim_header.rindex(b'\r\n ') dkim_header = dkim_header[:pos]+b'\r\n\t'+dkim_header[pos+3:] result = dkim.verify(dkim_header + sample_msg, dnsfunc=self.dnsfunc, minkey=512) self.assertTrue(result)
def send_mail(adress, maire={}, real_send=False): with open('mail.private', 'rb') as f: private_key = f.read() msg = MIMEMultipart() msg['Subject'] = 'Élection présidentielle française de 2017' msg['From'] = 'Vincent Lamotte <*****@*****.**>' msg['To'] = adress msg['Date'] = formatdate(localtime=True) msg['Message-Id'] = make_msgid(domain='vlamotte.fr') with open('template.jinja', 'r') as t: template = Template(t.read()) msg_text = MIMEText(template.render(maire=maire)) msg.attach(msg_text) with open('ProgrammeLamotte2017.pdf', 'rb') as pdf: msg_pdf = MIMEBase('application', "octet-stream") msg_pdf.set_payload(pdf.read()) encoders.encode_base64(msg_pdf) msg_pdf.add_header('Content-Disposition', 'attachment; filename="ProgrammeLamotte2017.pdf"') msg.attach(msg_pdf) msg_byte = bytes(msg.as_string(), encoding='utf-8') sign = dkim.sign(msg_byte, domain=b'vlamotte.fr', selector=b'mail', privkey=private_key, include_headers=[b'From', b'To', b'Date', b'Subject']) msg_byte = sign + msg_byte if real_send: try: result = server.sendmail(msg['From'], msg['To'], msg_byte) result = 'OK' except smtplib.SMTPServerDisconnected as e: result = 'KO' logging.warning('#' + str(e)) time.sleep(180) init() except smtplib.SMTPSenderRefused as e: result = 'KO' logging.warning('#' + str(e)) end() time.sleep(180) init() except Exception as e: result = 'KO' logging.warning('#' + str(e)) else: result = 'OK' return result
def test_altered_body_fails(self): # An altered body fails verification. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.net", self.key, signature_algorithm=b'ed25519-sha256') res = dkim.verify(sig + self.message + b"foo", dnsfunc=self.dnsfunc) self.assertFalse(res)
def test_rfc8032_verifies(self): # A message using RFC 8032 sample keys verifies after being signed. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message3, b"brisbane", b"football.example.com", self.rfckey, canonicalize=(header_algo, body_algo), signature_algorithm=b'ed25519-sha256') res = dkim.verify(sig + self.message3, dnsfunc=self.dnsfunc) self.assertTrue(res)
def dkim_sign(message): """ :returns: A signed email message if dkim package and settings are available. """ if not HAS_DKIM: return message if not (DKIM_DOMAIN and DKIM_PRIVATE_KEY): return message sig = dkim.sign(message, DKIM_SELECTOR, DKIM_DOMAIN, DKIM_PRIVATE_KEY, include_headers=DKIM_HEADERS) return sig + message
def test_tlsrpt_with_no_tlsrptsvc(self): # A tlsrpt signed with a key record without s=tlsrpt and tlsrpt=True should verify. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo)) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc, tlsrpt=True) self.assertTrue(res)
def test_double_previous_verifies(self): # A message previously signed using both rsa and ed25519 verifies after being signed. for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message3, b"test", b"football.example.com", self.key, canonicalize=(header_algo, body_algo), signature_algorithm=b'rsa-sha256') d = dkim.DKIM(self.message4) res = d.verify(dnsfunc=self.dnsfunc5) self.assertTrue(res)
def test_l_verify(self): # Sign with l=, add text, should verify for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo), length=True) self.message += b'added more text\n' res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertTrue(res)
def test_no_from_fails(self): # Body From is mandatory to be in the message and mandatory to sign sigerror = False sig = '' message = read_test_data('test_nofrom.message') selector = 'test' domain = 'example.com' identity = None try: sig = dkim.sign(message, selector, domain, read_test_data('test.private'), identity = identity) except dkim.ParameterError as x: sigerror = True self.assertTrue(sigerror)
def mail(useremail, donum, all_num, time_now): donum = donum + 1 try: message = MIMEText(deal_email_data.rand_words('txt'), 'plain', 'utf-8') sender = deal_email_data.senderemail() message['From'] = sender # 发送者 web = sender.split('@')[1] os.system('hostname ' + web) #sig = dkim.sign(msg, "s1", web, open("/default.private").read()) #msg = sig + sig = dkim.sign( message.as_string(), 'default', web, open( os.path.join("/etc/opendkim/keys/" + web + '/', 'default.private')).read()) message['DKIM-Signature'] = sig[len("DKIM-Signature: "):] message['Reply-To'] = sender message['from2'] = 'from2' message['To'] = useremail message['Subject'] = deal_email_data.rand_words('subject') message['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S +0800", time.localtime()) message['Message-ID'] = "<" + str(time.time()) + sender + ">" message['X-Mailer'] = 'Afterlogic webmail client' message_str = message.as_string().replace('From: ' + sender + '\n', '').replace( 'from2: from2\n', 'From: ' + sender + '\n') except: with open('res/useremail.txt', 'a+') as f: f.write('\n' + useremail) else: try: deal_email_data.sendmail(message['From'], message['To'], message_str) message_detail = message['From'] + ' sendto ' + useremail left = all_num - donum percent = (donum / all_num) * 100 timeuse = time.time() - time_now except smtplib.SMTPException as e: print e if e[0] == 550: with open('res/useremail.txt', 'a+') as f: f.write('\n' + useremail) else: record_process = str(percent) + '%' + ' deal ' + str( donum) + ' message left ' + str(left) + ' use ' + str( timeuse) + 's ' + message_detail + '\n' with open('res/progress.txt', 'a+') as f: f.write(record_process) print record_process.replace("\n", "")
def write_message(self, message): """Write signed message to output stream.""" msg = message.message() msg_data = msg.as_bytes(linesep='\r\n') signature = dkim.sign(msg_data, bytes(settings.DKIM_SELECTOR, 'ascii'), bytes(settings.DKIM_DOMAIN, 'ascii'), bytes(settings.DKIM_PRIVATE_KEY, 'ascii')) msg_data = signature + msg_data charset = msg.get_charset().get_output_charset() if msg.get_charset() else 'utf-8' msg_data = msg_data.decode(charset) self.stream.write('%s\n' % msg_data) self.stream.write('-' * 79) self.stream.write('\n')
def test_tlsrpt_ignore_l_sign(self): # For a tlsrpt, don't add l= when signing tlsrpt for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign(self.message, b"test", b"example.com", self.key, canonicalize=(header_algo, body_algo), length=True, tlsrpt=True) self.message += b'added more text' res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertFalse(res)
def dkim_sign(message): """Return signed email message if dkim package and settings are available.""" try: import dkim except ImportError: pass else: dkim_domain = getattr(settings, "DKIM_DOMAIN", None) dkim_key = getattr(settings, "DKIM_PRIVATE_KEY", None) dkim_selector = getattr(settings, "DKIM_SELECTOR", "ses") dkim_headers = getattr(settings, "DKIM_HEADERS", ("From", "To", "Cc", "Subject")) if dkim_domain and dkim_key: sig = dkim.sign(message, dkim_selector, dkim_domain, dkim_key, include_headers=dkim_headers) message = sig + message return message
def dkim_sign(message, dkim_domain=None, dkim_key=None, dkim_selector=None, dkim_headers=None): """Return signed email message if dkim package and settings are available.""" try: import dkim except ImportError: pass else: if dkim_domain and dkim_key: sig = dkim.sign(message, dkim_selector, dkim_domain, dkim_key, include_headers=dkim_headers) message = sig + message return message
def dkim_sign(message): """Return signed email message if dkim package and settings are available.""" try: import dkim except ImportError: pass else: dkim_domain = settings.DKIM_DOMAIN dkim_key = settings.DKIM_PRIVATE_KEY dkim_selector = settings.DKIM_SELECTOR dkim_headers = settings.DKIM_HEADERS if dkim_domain and dkim_key: sig = dkim.sign(message, dkim_selector, dkim_domain, dkim_key, include_headers=dkim_headers) message = sig + message return message
def dkim_sign(self, message): # Courtesy of http://djangosnippets.org/snippets/1995/ raw_message = message.message().as_string() if settings.DKIM_PRIVATE_KEY: included_headers = [ "Content-Type", "MIME-Version", "Content-Transfer-Encoding", "Subject", "From", "To" ] dkim_header = dkim.sign(raw_message, settings.DKIM_SELECTOR, settings.DKIM_DOMAIN, settings.DKIM_PRIVATE_KEY, include_headers=included_headers) raw_message = dkim_header + raw_message return raw_message
def examine(self, suspect): if not DKIMPY_AVAILABLE: suspect.debug("dkimpy not available, can not check") self._logger().error( "DKIM signing skipped - missing dkimpy library") return DUNNO message = suspect.get_source() domain = extract_from_domain(suspect) addvalues = dict(header_from_domain=domain) selector = apply_template( self.config.get(self.section, 'selector'), suspect, addvalues) if domain is None: self._logger().error( "%s: Failed to extract From-header domain for DKIM signing" % suspect.id) return DUNNO privkeyfile = apply_template( self.config.get(self.section, 'privatekeyfile'), suspect, addvalues) if not os.path.isfile(privkeyfile): self._logger().error("%s: DKIM signing failed for domain %s, private key not found: %s" % (suspect.id, domain, privkeyfile)) return DUNNO privkeycontent = open(privkeyfile, 'r').read() canH = Simple canB = Simple if self.config.get(self.section, 'canonicalizeheaders').lower() == 'relaxed': canH = Relaxed if self.config.get(self.section, 'canonicalizebody').lower() == 'relaxed': canB = Relaxed canon = (canH, canB) headerconfig = self.config.get(self.section, 'signheaders') if headerconfig is None or headerconfig.strip() == '': inc_headers = None else: inc_headers = headerconfig.strip().split(',') blength = self.config.getboolean(self.section, 'signbodylength') dkimhdr = sign(message, selector, domain, privkeycontent, canonicalize=canon, include_headers=inc_headers, length=blength, logger=suspect.get_tag('debugfile')) if dkimhdr.startswith('DKIM-Signature: '): dkimhdr = dkimhdr[16:] suspect.addheader('DKIM-Signature', dkimhdr, immediate=True)
def examine(self,suspect): if not DKIMPY_AVAILABLE: suspect.debug("dkimpy not available, can not check") suspect.set_tag('DKIMVerify.skipreason','dkimpy library not available') return DUNNO starttime=time.time() message=suspect.get_source() domain=self.get_header_from_domain(suspect) addvalues=dict(header_from_domain=domain) selector=apply_template(self.config.get(self.section,'selector'),suspect,addvalues) if domain==None: self._logger().error("%s: Failed to extract From-header domain for DKIM signing"%suspect.id) return DUNNO privkeyfile=apply_template(self.config.get(self.section,'privatekeyfile'), suspect,addvalues) if not os.path.isfile(privkeyfile): self._logger().error("%s: DKIM signing failed for domain %s, private key not found: %s"%(suspect.id,domain,privkeyfile)) return DUNNO privkeycontent=open(privkeyfile,'r').read() canH=Simple canB=Simple if self.config.get(self.section,'canonicalizeheaders').lower()=='relaxed': canH=Relaxed if self.config.get(self.section,'canonicalizebody').lower()=='relaxed': canB=Relaxed canon=(canH,canB) headerconfig=self.config.get(self.section,'signheaders') if headerconfig==None or headerconfig.strip()=='': inc_headers=None else: inc_headers=headerconfig.strip().split(',') blength=self.config.getboolean(self.section,'signbodylength') dkimhdr=sign(message, selector, domain, privkeycontent, canonicalize=canon, include_headers=inc_headers, length=blength, logger=suspect.get_tag('debugfile')) if dkimhdr.startswith('DKIM-Signature: '): dkimhdr=dkimhdr[16:] suspect.addheader('DKIM-Signature',dkimhdr,immediate=True) endtime=time.time() difftime=endtime-starttime suspect.tags['DKIMSign.time']="%.4f"%difftime
def _send(self, email_message): """A helper method that does the actual sending + DKIM signing.""" if not email_message.recipients(): return False try: message_string = email_message.message().as_string() signature = dkim.sign(message_string, settings.DKIM_SELECTOR, settings.DKIM_DOMAIN, settings.DKIM_PRIVATE_KEY) self.connection.sendmail(email_message.from_email, email_message.recipients(), signature+message_string) except: if not self.fail_silently: raise return False return True
def mail(useremail, donum, dealnum, all_num, time_now): try: os.system('hostname ' + iptable.hostwebone()) sender = senderemail() web = sender.split('@')[1] message_str = iptable.mail_detail(sender, useremail) sig = dkim.sign( message_str, 'default', web, open( os.path.join("/etc/opendkim/keys/" + web + '/', 'default.private')).read()) message_str = 'DKIM-Signature: ' + sig[len("DKIM-Signature: " ):] + message_str except: with open('res/useremail.txt', 'a+') as f: f.write('\n' + useremail) else: try: sendmaildeal(sender, useremail, message_str) message_detail = sender + ' sendto ' + useremail timeuse = time.time() - time_now except smtplib.SMTPException as e: print e if e[0] == 550: with open('res/useremail.txt', 'a+') as f: f.write('\n' + useremail) else: with open('res/useremail.txt', 'a+') as f: f.write('\n' + useremail) except: with open('res/useremail.txt', 'a+') as f: f.write('\n' + useremail) else: donum = donum + 1 left = all_num - donum timeper = timeuse / (dealnum + 1) oneday_num = str(int((3600 * 24) / int(timeper))) record_process = str(int(timeper)) + '秒/每封 ' + ' 处理 ' + str( dealnum + 1 ) + ' 成功 ' + str(donum) + ' 剩余 ' + str(left) + ' 耗时 ' + str( int(timeuse / 60)) + '分钟 ' + oneday_num + '封/天 ' + message_detail + '\n' with open('res/static/progress.txt', 'a+') as f: f.write(record_process) print record_process.replace("\n", "") return donum
def _send(self, email_message): """A helper method that does the actual sending + DKIM signing.""" if not email_message.recipients(): return False try: message_string = email_message.message().as_string() signature = dkim.sign(message_string, settings.DKIM_SELECTOR, settings.DKIM_DOMAIN, settings.DKIM_PRIVATE_KEY) self.connection.sendmail(email_message.from_email, email_message.recipients(), signature + message_string) except: if not self.fail_silently: raise return False return True
def add_dkim_signature(msg: Message, email_domain: str): delete_header(msg, "DKIM-Signature") # Specify headers in "byte" form # Generate message signature sig = dkim.sign( to_bytes(msg), DKIM_SELECTOR, email_domain.encode(), DKIM_PRIVATE_KEY.encode(), include_headers=DKIM_HEADERS, ) sig = sig.decode() # remove linebreaks from sig sig = sig.replace("\n", " ").replace("\r", "") msg["DKIM-Signature"] = sig[len("DKIM-Signature: ") :]
def test_dkim_signature_canonicalization(self): # <https://bugs.launchpad.net/ubuntu/+source/pydkim/+bug/587783> # Relaxed-mode header signing is wrong # <https://bugs.launchpad.net/dkimpy/+bug/939128> # Simple-mode signature header verification is wrong # (should ignore FWS anywhere in signature tag: b=) sample_msg = b"""\ From: [email protected] To: [email protected] Subject: this is my test message """.replace(b'\n', b'\r\n') sample_privkey = b"""\ fL+5V9EquCZAovKik3pA6Lk9zwCzoEtjIuIqK9ZXHHA=\ """ sample_pubkey = """\ yi50DjK5O9pqbFpNHklsv9lqaS0ArSYu02qp1S0DW1Y=\ """ for header_mode in [dkim.Relaxed, dkim.Simple]: dkim_header = dkim.sign(sample_msg, b'example', b'canonical.com', sample_privkey, canonicalize=(header_mode, dkim.Relaxed), signature_algorithm=b'ed25519-sha256') # Folding dkim_header affects b= tag only, since dkim.sign folds # sig_value with empty b= before hashing, and then appends the # signature. So folding dkim_header again adds FWS to # the b= tag only. This should be ignored even with # simple canonicalization. # http://tools.ietf.org/html/rfc4871#section-3.5 signed = dkim.fold(dkim_header) + sample_msg result = dkim.verify(signed, dnsfunc=self.dnsfunc) self.assertTrue(result) dkim_header = dkim.fold(dkim_header) # use a tab for last fold to test tab in FWS bug pos = dkim_header.rindex(b'\r\n ') dkim_header = dkim_header[:pos] + b'\r\n\t' + dkim_header[pos + 3:] result = dkim.verify(dkim_header + sample_msg, dnsfunc=self.dnsfunc) self.assertTrue(result)
def test_no_from_fails(self): # Body From is mandatory to be in the message and mandatory to sign sigerror = False sig = '' message = read_test_data('test_nofrom.message') selector = 'test' domain = 'example.net' identity = None try: sig = dkim.sign(message, selector, domain, read_test_data('ed25519test.key'), identity=identity, signature_algorithm=b'ed25519-sha256') except dkim.ParameterError as x: sigerror = True self.assertTrue(sigerror)
def dkim_sign(message): """Return signed email message if dkim package and settings are available.""" try: import dkim except ImportError: pass else: dkim_domain = getattr(settings, "DKIM_DOMAIN", None) dkim_key = getattr(settings, 'DKIM_PRIVATE_KEY', None) dkim_selector = getattr(settings, 'DKIM_SELECTOR', 'ses') dkim_headers = getattr(settings, 'DKIM_HEADERS', ('From', 'To', 'Cc', 'Subject')) if dkim_domain and dkim_key: sig = dkim.sign(message, dkim_selector, dkim_domain, dkim_key, include_headers=dkim_headers) message = sig + message return message
def _send(self, email_message): """A helper method that does the actual sending + DKIM signing. """ if not email_message.recipients(): return False try: message = email_message.message().as_bytes(linesep='\r\n') signature = dkim.sign(message, bytes(settings.DKIM_SELECTOR, 'utf8'), bytes(settings.DKIM_DOMAIN, 'utf8'), settings.DKIM_PRIVATE_KEY, include_headers=[b'from',]) self.connection.sendmail(email_message.from_email, email_message.recipients(), signature+message) except: # raise when sending email with fail_silently turned off if not self.fail_silently: raise return False return True
def dkim_sign(message): """Return signed email message if dkim package and settings are available.""" if "DKIM_DOMAIN" not in config: return message try: import dkim except ImportError: pass else: dkim_domain = config["DKIM_DOMAIN"] dkim_key = config["DKIM_PRIVATE_KEY"] dkim_selector = config["DKIM_SELECTOR"] dkim_headers = config["DKIM_HEADERS"] if dkim_domain and dkim_key: sig = dkim.sign(message, dkim_selector, dkim_domain, dkim_key, include_headers=dkim_headers) message = sig + message return message
def test_multiple_from_fails(self): # <https://bugs.launchpad.net/dkimpy/+bug/644046> # additional From header fields should cause verify failure hfrom = b'From: "Resident Evil" <*****@*****.**>\r\n' h,b = self.message.split(b'\n\n',1) for header_algo in (b"simple", b"relaxed"): for body_algo in (b"simple", b"relaxed"): sig = dkim.sign( self.message, b"test", b"example.com", self.key) # adding an unknown header still verifies h1 = h+b'\r\n'+b'X-Foo: bar' message = b'\n\n'.join((h1,b)) res = dkim.verify(sig+message, dnsfunc=self.dnsfunc) self.assertTrue(res) # adding extra from at end should not verify h1 = h+b'\r\n'+hfrom.strip() message = b'\n\n'.join((h1,b)) res = dkim.verify(sig+message, dnsfunc=self.dnsfunc) self.assertFalse(res) # add extra from in front should not verify either h1 = hfrom+h message = b'\n\n'.join((h1,b)) res = dkim.verify(sig+message, dnsfunc=self.dnsfunc) self.assertFalse(res)
def testDKIM(self): privKey = """-----BEGIN RSA PRIVATE KEY----- MIICXQIBAAKBgQDe/Uq1hSUvQStzcpHTiGymukXBEQOxrk8uu4/5aT5AHi6HC1MY 8fiCoi6sdUL/3VLLvqH0Bb2H5jqz7KAs7wwNRJvAXPzs8MTNFyeg6702H+R5t9/W h0mBBwuD9yaYWuOsXrhn9mq7nWcWhiDe1HGLUa1nte/MlhR6MsPm1GOHhwIDAQAB AoGAbo3NuGkmomMBE9+9hM6ib5bydmHlHvZ4s4ayPsl633cXQkTPEhMFTl7yHPaW HRyxq+n7iWw/J11xxTqPvzdVFWHuMA7oTfz+3gdKlbEnbfKFGo4rboYNnOBpYlKg mnmPywWjMTl3av/rXHNAgjHi1ZREORFKXwjbQGK8y2ExXCECQQD07IIT0NCOOJej UUr9Qt94NJASsYbD4/fMpnExYwv8QKT2YxTIXtFAlXi5azRpkAvxli0r+qZLklsh /gQIGyHjAkEA6RLZFMJelrLy8BkLzyyY0nYKte50sAuJToPv2nJK9UNHyEfIQSve VDJNimzSdGwDHfUGHeVDYsnnXK81qv8lDQJBAIe4Nyx73dWxjnW1qnRFBkg5+Ewj i6YpQTtqT/cqB4401DSkGvQddp7vNQKqYVTNuZCZw1ZHgrcF1vIzLFDBmDkCQBNA KEfrqe5eh2xHVU9eSp0PfOD7+g1UVpnykcwEJqbNUM99BlBDtFBV+0uUo2lURomh 5Ehx2Df/nylrm04tVr0CQQDP1g/PckPzOzKVoppKiFFYGzd/W0NF0dUwA4r0Qv/Y v8T0kOg+O9/myj6AFk1G56PQRT4Fjlb/ey8ALGkjyKYO -----END RSA PRIVATE KEY-----""" body = """Hi There, This is a simple message that will be signed by pydkim --The signer """ msg = Message() msg['From'] = 'Sender <*****@*****.**>' msg['To'] = 'Recipient <*****@*****.**>' msg['Subject'] = 'Test message' msg.set_payload(body) headers = ['To', 'From', 'Subject'] email = msg.as_string() import dkim sig = dkim.sign(email, 'default', 'topdog-software.com', privKey, include_headers=headers) print sig, email
def send_messages(self, email_messages): """Sends one or more EmailMessage objects and returns the number of email messages sent. """ if not email_messages: return new_conn_created = self.open() if not self.connection: # Failed silently return num_sent = 0 source = getattr(settings, 'AWS_SES_RETURN_PATH', None) for message in email_messages: # Automatic throttling. Assumes that this is the only SES client # currently operating. The AWS_SES_AUTO_THROTTLE setting is a # factor to apply to the rate limit, with a default of 0.5 to stay # well below the actual SES throttle. # Set the setting to 0 or None to disable throttling. if self._throttle: global recent_send_times now = datetime.now() # Get and cache the current SES max-per-second rate limit # returned by the SES API. rate_limit = self.get_rate_limit() # Prune from recent_send_times anything more than a few seconds # ago. Even though SES reports a maximum per-second, the way # they enforce the limit may not be on a one-second window. # To be safe, we use a two-second window (but allow 2 times the # rate limit) and then also have a default rate limit factor of # 0.5 so that we really limit the one-second amount in two # seconds. window = 2.0 # seconds window_start = now - timedelta(seconds=window) new_send_times = [] for time in recent_send_times: if time > window_start: new_send_times.append(time) recent_send_times = new_send_times # If the number of recent send times in the last 1/_throttle # seconds exceeds the rate limit, add a delay. # Since I'm not sure how Amazon determines at exactly what # point to throttle, better be safe than sorry and let in, say, # half of the allowed rate. if len(new_send_times) > rate_limit * window * self._throttle: # Sleep the remainder of the window period. td = now - new_send_times[0] seconds = td.days * 24 * 60 * 60 + td.seconds delay = window - seconds if delay > 0: sleep(delay) recent_send_times.append(now) # end of throttling try: raw_message = message.message().as_string() if self._dkim_signature: import dkim sig = dkim.sign( raw_message, self._dkim_selector, self._dkim_domain, self._dkim_private_key, include_headers=[] ) raw_message = sig + raw_message response = self.connection.send_raw_email( source=source or message.from_email, destinations=message.recipients(), raw_message=raw_message, ) message.extra_headers['status'] = 200 message.extra_headers['message_id'] = response[ 'SendRawEmailResponse']['SendRawEmailResult']['MessageId'] message.extra_headers['request_id'] = response[ 'SendRawEmailResponse']['ResponseMetadata']['RequestId'] num_sent += 1 except SESConnection.ResponseError, err: # Store failure information so to post process it if required error_keys = ['status', 'reason', 'body', 'request_id', 'error_code', 'error_message'] for key in error_keys: message.extra_headers[key] = getattr(err, key, None) if not self.fail_silently: raise
import sys import dkim if len(sys.argv) < 4 or len(sys.argv) > 5: print("Usage: dkimsign.py selector domain privatekeyfile [identity]", file=sys.stderr) sys.exit(1) if sys.version_info[0] >= 3: # Make sys.stdin and stdout binary streams. sys.stdin = sys.stdin.detach() sys.stdout = sys.stdout.detach() selector = sys.argv[1].encode('ascii') domain = sys.argv[2].encode('ascii') privatekeyfile = sys.argv[3] if len(sys.argv) > 5: identity = sys.argv[4].encode('ascii') else: identity = None message = sys.stdin.read() try: sig = dkim.sign(message, selector, domain, open(privatekeyfile, "rb").read(), identity = identity) sys.stdout.write(sig) sys.stdout.write(message) except Exception as e: print(e, file=sys.stderr) sys.stdout.write(message)
def test_badly_encoded_domain_fails(self): # Domains should be ASCII. Bad ASCII causes verification to fail. sig = dkim.sign(self.message, b"test", b"example.com\xe9", self.key) res = dkim.verify(sig + self.message, dnsfunc=self.dnsfunc) self.assertFalse(res)
except IOError, ex: log.error("file %s is not readable", keyfile) log.error(ex.message) sys.exit(2) # compose message msg = Message() msg['From'] = sender msg['To'] = recipient msg['Subject'] = subject msg.set_payload(body) # sign message email = msg.as_string() sig = sign(email, selector, domain, private_key) dkimmail = sig + email log.info("e-mail message is:") log.info(dkimmail) if printonly: print dkimmail else: try: server = smtplib.SMTP('localhost') # TODO: check loglevel if verbose: log.info("smtp debug level was set to 1") server.set_debuglevel(1) server.sendmail(sender, recipient, dkimmail)