1

I'm encrypting (no need to sign) a message in python using a public key .pem file that I extracted from a .pfx file via the following OpenSSL commands:

openssl pkcs12 -in certificate.pfx -nokeys -out publickey.crt
openssl x509 -in publickey.crt -pubkey -noout > publickey.pem

However, when I send the email via the following python code:

import sys
import smtplib
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
import smtplib
from email.mime.base import MIMEBase
from email import encoders
from email.message import EmailMessage
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

# Replace with your email server credentials and recipient email address
smtp_server = 'smtp.sendgrid.net'
smtp_port = 587
username = 'apikey'
password = 'sendgrid apikey'
recipient = 'recipient@gmail.com'
from_email = 'sender@example.com'

# Build the message
message = 'This text is encrypted, so you should not see it.'

# Read the public key from the file
with open('publickey.pem', 'rb') as key_file:
    public_key = serialization.load_pem_public_key(key_file.read(), backend=default_backend())

# Encrypt the message using the public key
encrypted_msg = public_key.encrypt(
    message.encode(),
    padding.OAEP(
        mgf=padding.MGF1(algorithm=hashes.SHA256()),
        algorithm=hashes.SHA256(),
        label=None
    )
)

# Use the encrypted message as the content of the email
msg = MIMEApplication(encrypted_msg, _subtype='pkcs7-mime')
msg.set_type("application/pkcs7-mime; name=smime.p7m; smime-type=enveloped-data")
msg.add_header('Content-Disposition', 'attachment', filename='smime.p7m')
msg['From'] = from_email
msg['To'] = recipient
msg['Subject'] = 'Encrypted email s/mime'

# Send the email
try:
    with smtplib.SMTP(smtp_server, smtp_port) as server:
        server.starttls()
        server.login(username, password)
        server.sendmail(from_email, recipient, msg.as_string())
        print("Email sent successfully!")
except Exception as e:
    print("Error while sending email:", e)

I get an email in my Thunderbird client with the following S/MIME padlock icon: S/MIME red padlock

I have the certificate for that public key used for encryption installed under Thunderbird's "Manage Certificate settings" so I just can't figure out what is going on. This is the message source of the emails generated by the above code:

Content-Transfer-Encoding: base64
MIME-Version: 1.0
Content-Type: application/pkcs7-mime; name=smime.p7m; smime-type=enveloped-data
Content-Disposition: attachment; filename="smime.p7m"
From: sender@example.com
Subject: Encrypted email s/mime
Message-ID: <2yanN0AkR5y9aZkdKl_Umg@geopod-ismtpd-2>
Date: Tue, 28 Mar 2023 06:52:59 +0000 (UTC)
X-Feedback-ID: 8137588:SG
X-SG-EID: 
 =?us-ascii?Q?nNFctdm0BWd6iTjLSzehWeQnMAXb40QT9vsis7KAAy2HyIwEYpZgT6S7yNu6sf?=
 =?us-ascii?Q?QfsDPx0iJwkBMObbzQjwXMPzUWpzqt42mNuEUvK?=
 =?us-ascii?Q?xx2sJdAb9NFhjmou4ynkknKQ6V8eKGJNhKL9KgI?=
 =?us-ascii?Q?8tcAkXP+Rf8eTiWc4WaF8mjqEAnjZBVfiyub+d7?=
 =?us-ascii?Q?Kxtn8tG3FxHOrTqQDn12wR2P0PrMnMCdhWvGUhU?=
 =?us-ascii?Q?Y5Q+=2F8KJ0WEtIzQAxjjbb3jcfc2x2ik+dhgpP0?=
To: recipient@gmail.com
X-Entity-ID: TD8cTk3vN3Zbd6Fq4hlbCQ==

gKfsZnJnEFI0Nb9UqM6aV7JYzpqjb+UOfSISazoDeEaQDdqo/piZeRrDqkGqjkOruL4N+XQTIxD3
mJSt2mffVNyEos6Yek6mlngA20Qli+lA27VVR2ihB7nA8GRN55oWe/HIYfFVgA1zA5+zAALzA3nG
oNjCcXPkAyptXw9B8Lcd2J5oOxvlXAv97PvtaqK+6QTvoj8jFkJtDvkoTEWFathHbbcLNPc9Sa53
77vQFbXXYJpOn9ds8IcAsQRmU+r0pmuQq6Hf23DmvF1Z+dMHpcNl/wR79Y6fYf8FGjavkZRS/l+U
Swp6DJb31fQuGacRukmh7wnik0+AxOM9ou+16w==

I have tried using a different library (Chilkat) with node.js, passing in a certificate.pem file instead of a public key and Thunderbird was able to decrypt the message with the same private key installed in Thunderbird, but since chilkat is a pay to use library I'm not able to use it so I need my own implementation of s/mime encryption.

I have also tried passing a certificate.pem file to my python code but was also seeing the same s/mime error icon: S/MIME padlock error text

I was also able to decrypt the content of the email using the private key and OpenSSL commands, so the key pair match.

I'm not able to use Gmail or Outlook since they are paid email clients which is outside of the scope for this use case.

So is this something to do with my code or Thunderbird? Any suggestions are appreciated.

  • You say you have the certificate installed, but have you installed the private key, which is required to decrypt? Please update your question with a step by step description how you have set up your S/MIME certificate in Thunderbird, as I suspect that's where the problem lies. – not2savvy Mar 28 '23 at 08:24
  • Apologies for not elaborating on that part. I have tried installing the private key in a couple of ways, and both have failed; I installed the certificate and private key via a PFX file, so Thunderbird added the private key and certificates, then I added just the private key PKCS12 file and it didn't work either. – David Sierra Mar 28 '23 at 09:17
  • Trying to reproduce with your code, can you help me understand what public_key.encrypt() exactly expects and does? Can you link to its docs? I'm getting `from cryptography.hazmat.bindings._openssl import ffi, lib ModuleNotFoundError: No module named '_cffi_backend` when I run the code. – not2savvy Mar 29 '23 at 13:27
  • When you decrypt the mail using openssl, did you decrypt the email with `openssl smime -decrypt` or just the encrypted attachment? – not2savvy Mar 29 '23 at 13:32
  • For sure, the encrypt() method is using the cryptography library to encrypt the message, docs here: https://cryptography.io/en/latest/hazmat/primitives/asymmetric/rsa/#encrypting As for the error you get... it's weird, I didn't have to import that module, but it's possible your installation of cryptography didn't include the cffi import, so you might have to do a `pip install cffi` or a clean install of cryptography again, but that is just my guess – David Sierra Mar 29 '23 at 17:22
  • And as for the decrypting, you just gave me an idea and I went ahead and decrypted the email that was successfully being decrypted by Thunderbird and I see this: Content-Type: text/plain; charset=us-ascii; format=flowed Content-Transfer-Encoding: 7bit This is a digitally encrypted mail So now I'm going to try adding those headers to my envelope and test it out, really appreciate all of the guidance! – David Sierra Mar 29 '23 at 17:24
  • If I understand it correctly, [this cryptography issue](https://github.com/pyca/cryptography/issues/5488) seems to suggests that the `encrypt()` method you use does not create the PKCS#7 object required for S/MIME. – not2savvy Mar 31 '23 at 09:50

1 Answers1

0

I think your issue is here:

# Build the message
message = 'This text is encrypted, so you should not see it.'

Thunderbird (or any other email client) expects a MIME message inside the encrypted part, which means it needs to have headers with at least its MIME content type. I assume that Thunderbird can in fact decrypt the encrypted part, but then it just doesn't know what to do with it.

So in your case, try to replace the above code by:

# Build the message
message = ('MIME-Version: 1.0\n'
'Content-Type: text/plain; charset=UTF-8\n'
'Content-Transfer-Encoding: 7bit\n'    
'\n'
'This text is encrypted, so you should not see it.')

For a more detailed answer about how to handle MIME headers when encrypting emails, you may want to read this answer, too.

not2savvy
  • 2,902
  • 3
  • 22
  • 37
  • Thanks for the guidance. I've tried modifying the code as you mention, but I just get the same result, but I'll keep looking into this to see if I can make it work. – David Sierra Mar 29 '23 at 04:38