部门某招投标信息爬虫由本人负责维护,天天定时爬取数据并经过公司邮箱(smtp)发送给各位大佬。某天公司的邮箱忽然升级为只能使用我的证书加密的邮件才能够发送邮件,因此研究了一下相关技术html
S/MIME是Secure/Multipurpose Internet Mail Extensions (安全多用途互联网邮件扩展协议)的缩写,是采用PKI技术的用数字证书给邮件主体签名和加密的国际标准协议。1992年,MIME(多用途互联网邮件扩展)协议编撰完成,用于互联网邮件服务器和网关之间通讯。该标准方法支持非ASCII编码的附件格式,意味着你能够发送附件并保证文件能够送达另外一端,可是附件有时会被篡改,没法确保邮件机密性和完整性。1995年,S/MIME(安全/多用途互联网邮件扩展)协议V1版本开发问世,对安全方面的功能进行了扩展,提供数字签名和邮件加密功能,邮件加密用来保护电子邮件的内容,数字签名用于验证发件人身份,防止身份冒用,并保护电子邮件完整性。1998年和1999年相继出台V2/V3版本并提交IETF造成系列RFC国际标准。node
未经S/MIME加密的邮件请求头python
Content-Type: multipart/mixed; boundary="===============1169690444==" MIME-Version: 1.0 Subject: =?utf-8?b?dGhpcyBpcyBmb3IgdGVzdA==?= From: potatso@xxx.com To: potatso@xxx.com --===============1169690444== Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: base64 base64
通过S/MIME处理后的邮件体编程
Subject: =?utf-8?b?dGhpcyBpcyBmb3IgdGVzdA==?= From: potatso@xxx.com To: potatso@xxx.com MIME-Version: 1.0 Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name=smime.p7m Content-Transfer-Encoding: base64 Content-Disposition: attachment; filename=smime.p7m MIIHvQYJKoZIhvcNAQcDoIIHrjCCB6oCAQAxgdIwgc8CAQAwOjAuMQswCQYDVQQG
咱们能够看见其中的区别,主要是邮件请求头的改变。下面咱们来实践一下安全
为了对邮件进行S/MIME加密,咱们首先要将p12证书文件转换为PEM证书文件。在这里不须要纠结p12证书文件与pem证书文件的区别,只须要知道SMIM加密或者签名须要pem证书文件服务器
下面咱们来讨论一下如何将p12证书文件转换,共有两种方法。注意,若是p12证书有密码的话,须要知道密码才能够进行下面的转换app
从p12中导出pem证书dom
openssl pkcs12 -in path.p12 -out newfile.crt.pem -clcerts -nokeys
测试
从p12中导出私钥编码
openssl pkcs12 -in path.p12 -out newfile.key.pem -nocerts -nodes
运行以下
demo# openssl pkcs12 -in liang_zhibang2020.p12 -out newfile.crt.pem -clcerts -nokeys Enter Import Password:
使用 openssl x509 -noout -text -in newfile.crt.pem
查看一下刚才导出的pem证书
root@LAPTOP-1KRDI4T2:/mnt/c/Users/liang/PycharmProjects/demo# openssl x509 -noout -text -in newfile.crt.pem Certificate: Data: Version: 3 (0x2) Serial Number: 7969397651085804113 (0x6e98fcf0a2cd4251) Signature Algorithm: sha1WithRSAEncryption .............
这种不如第一种简便,代码以下
import OpenSSL from OpenSSL import crypto # open it, using password. Supply/read your own from stdin. p12 = crypto.load_pkcs12(open("cert.p12", 'rb').read(), b"passwd") # get various properties of said file. # note these are PyOpenSSL objects, not strings although you # can convert them to PEM-encoded strings. print(p12.get_certificate()) # (获取证书 print(p12.get_privatekey()) # 获取私钥 print(p12.get_ca_certificates()) # 查看ca chain public_key = OpenSSL.crypto.dump_publickey( OpenSSL.crypto.FILETYPE_PEM, p12.get_certificate().get_pubkey()) privatekey = crypto.dump_privatekey(crypto.FILETYPE_PEM, p12.get_privatekey())
邮件部分正常生成便可。只须要在smtpObj.sendmail(sender, receivers, msg)
到处理便可。在这里咱们使用smime库来完成工做
pip install smime
with open("newfile.crt.pem", "rb") as f
smtpObj.sendmail(sender, receivers, smime.encrypt(msg.as_string(), f.read()))
完整代码以下
with open("newfile.crt.pem", "rb") as f: print(smime.encrypt(msg.as_string(), f.read())) smtpObj.sendmail(sender, receivers, smime.encrypt(msg.as_string(), f.read())) print("邮件发送成功")
在这里咱们须要使用M2Crypt库完成工做,固然M2Crypt也能够对邮件加密,验证等,可是安装过于繁琐,故未经测试,且咱们不须要对邮件签名
from M2Crypto import BIO, Rand, SMIME def makebuf(text): return BIO.MemoryBuffer(text) # Make a MemoryBuffer of the message. buf = makebuf('a sign of our times') # Seed the PRNG. Rand.load_file('randpool.dat', -1) # Instantiate an SMIME object; set it up; sign the buffer. s = SMIME.SMIME() s.load_key('signer_key.pem', 'signer.pem') p7 = s.sign(buf) p7 now contains a PKCS #7 signature blob wrapped in an M2Crypto.SMIME.PKCS7 object. Note that buf has been consumed by sign() and has to be recreated if it is to be used again. We may now send the signed message via SMTP. In these examples, we shall not do so; instead, we'll render the S/MIME output in mail-friendly format, and pretend that our messages are sent and received correctly. # Recreate buf. buf = makebuf('a sign of our times') # Output p7 in mail-friendly format. out = BIO.MemoryBuffer() out.write('From: sender@example.dom\n') out.write('To: recipient@example.dom\n') out.write('Subject: M2Crypto S/MIME testing\n') s.write(out, p7, buf) print out.read() # Save the PRNG's state. Rand.save_file('randpool.dat')