部門某招投標信息爬蟲由本人負責維護,天天定時爬取數據並經過公司郵箱(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')