My name is James and I have been trying to sent myself an encrypted message through email which gets decrypted on my phone by the Autocrypt protocol in Delta Chat using python. However when I export the key in Delta Chat, save it as public_key.asc and use the following code with my personal webmail to send the email, Delta Chat can’t decrypt it and I just see encrypted text on both my phone and email.
The python code:
import gnupg
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
# Initialize GPG (assuming gnupg is already set up)
gpg = gnupg.GPG()
# Define file paths
script_directory = os.path.dirname(os.path.abspath(__file__))
input_file_path = os.path.join(script_directory, "example.txt")
output_encrypted_file_path = os.path.join(script_directory, "encrypted.asc")
public_key_file_path = os.path.join(script_directory, "public_key.asc")
# Import the public key from the file
with open(public_key_file_path, 'r') as public_key_file:
public_key_data = public_key_file.read()
import_result = gpg.import_keys(public_key_data)
# Email body text to encrypt
email_body = "This is a secure message encrypted for Autocrypt-compatible clients. Please see the attached encrypted file."
# Encrypt the email body text with the public key
encrypted_body = gpg.encrypt(email_body, recipients=[import_result.fingerprints[0]], always_trust=True)
if not encrypted_body.ok:
print("Email body encryption failed!")
print(f"Status: {encrypted_body.status}")
print(f"Error: {encrypted_body.stderr}")
exit(1)
# Encrypt the file with the public key
with open(input_file_path, 'rb') as input_file:
encrypted_data = gpg.encrypt_file(input_file, recipients=[import_result.fingerprints[0]],
always_trust=True, output=output_encrypted_file_path)
if not encrypted_data.ok:
print("File encryption failed!")
print(f"Status: {encrypted_data.status}")
print(f"Error: {encrypted_data.stderr}")
exit(1)
# Set up the email details
sender_email = "[youremail@example.com](mailto:youremail@example.com)"
recipient_email = "[youremail@example.com](mailto:youremail@example.com)" # Same as sender
subject = "Encrypted message for Autocrypt"
smtp_server = "[smtp.your-email-provider.com]"
smtp_port = 465
email_password = "your-email-password" # Use App-specific password if needed
# Construct MIME-compliant encrypted email
msg = MIMEMultipart("encrypted", protocol="application/pgp-encrypted")
msg['From'] = sender_email
msg['To'] = recipient_email
msg['Subject'] = subject
# PGP headers required by Autocrypt
pgp_header = MIMEText("Version: 1\n", _subtype="pgp-encrypted")
msg.attach(pgp_header)
# Add the encrypted email body content
encrypted_body_part = MIMEText(str(encrypted_body), _subtype="octet-stream")
encrypted_body_part.add_header('Content-Disposition', 'inline')
msg.attach(encrypted_body_part)
# Add the encrypted file content as an attachment
with open(output_encrypted_file_path, 'rb') as encrypted_file:
encrypted_part = MIMEApplication(encrypted_file.read(), _subtype="octet-stream")
encrypted_part.add_header('Content-Disposition', 'attachment', filename="encrypted.asc")
msg.attach(encrypted_part)
# Send the email via SMTP
try:
with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:
server.login(sender_email, email_password)
server.sendmail(sender_email, recipient_email, msg.as_string())
print("Encrypted email sent successfully!")
except Exception as e:
print(f"Failed to send email: {e}")
Could you please help me or provide directions on what I have to do to improve the code so Delta Chat can decrypt it? What do I have to do make sure Delta Chat can decrypt it, maybe add extra headers? Or do I need get different keys or upload my own? Much appreciated!
Delta Chat only decrypts messages that follow PGP/MIME specification. The message must be a multipart/encrypted message with exactly two parts: application/pgp-encrypted with Version: 1 and application/octet-stream (constructed with MIMEApplication in Python) part.
In your code you encrypt message body and attachment separately and attach them to the message, so the message ends up having 3 parts. Instead of doing this, construct a multipart/mixed message with the body part and attachment part, then encrypt it as a whole and put the result into the second part of application/pgp-encrypted.
Makes sense but something weird happens, I dont get anything on my phone except for: Encrypted Email (PGP / MIME Compliant) - Version: 1. The part thats encrypted doesnt show up at all like it used to with the previous code which shows that something happened and it doesn’t get displayed in Delta Chat. The attachment doesn’t show up as well.
import gnupg
import os
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
# Initialize GPG
gpg = gnupg.GPG()
# Define file paths
script_directory = os.path.dirname(os.path.abspath(__file__))
input_file_path = os.path.join(script_directory, "example.txt")
public_key_file_path = os.path.join(script_directory, "public_key.asc")
# Import the public key from the file
with open(public_key_file_path, 'r') as public_key_file:
public_key_data = public_key_file.read()
import_result = gpg.import_keys(public_key_data)
if not import_result.fingerprints:
print("Failed to import public key!")
exit(1)
# Prepare the inner multipart/mixed message
mixed_message = MIMEMultipart("mixed")
mixed_message['From'] = "youremail@example.com"
mixed_message['To'] = "youremail@example.com"
mixed_message['Subject'] = "Encrypted PGP/MIME message for Delta Chat"
# Email body part
email_body = "This is a secure message encrypted for Autocrypt-compatible clients."
body_part = MIMEText(email_body, "plain")
mixed_message.attach(body_part)
# File attachment part
with open(input_file_path, 'rb') as input_file:
attachment_part = MIMEApplication(input_file.read(), _subtype="octet-stream")
attachment_part.add_header('Content-Disposition', 'attachment', filename="example.txt")
mixed_message.attach(attachment_part)
# Encrypt the entire mixed message as a single block
encrypted_data = gpg.encrypt(mixed_message.as_string(), recipients=[import_result.fingerprints[0]], always_trust=True)
if not encrypted_data.ok:
print("Encryption failed!")
print(f"Status: {encrypted_data.status}")
print(f"Error: {encrypted_data.stderr}")
exit(1)
# Construct the final PGP/MIME-compliant message
final_msg = MIMEMultipart("encrypted", protocol="application/pgp-encrypted")
final_msg['From'] = "youremail@example.com"
final_msg['To'] = "youremail@example.com"
final_msg['Subject'] = "Encrypted Email (PGP/MIME Compliant)"
# Add the PGP version part
pgp_header_part = MIMEText("Version: 1", _subtype="pgp-encrypted")
final_msg.attach(pgp_header_part)
# Add the encrypted content as a single application/octet-stream part
encrypted_payload = MIMEApplication(encrypted_data.data, _subtype="octet-stream")
final_msg.attach(encrypted_payload)
# Send the email via SMTP
smtp_server = "smtp.your-email-provider.com"
smtp_port = 465
email_password = "your-email-password"
try:
with smtplib.SMTP_SSL(smtp_server, smtp_port) as server:
server.login("youremail@example.com", email_password)
server.sendmail("youremail@example.com", "youremail@example.com", final_msg.as_string())
print("Encrypted email sent successfully!")
except Exception as e:
print(f"Failed to send email: {e}")
This code produces first part that has Content-Type: text/pgp-encrypted; charset="us-ascii".
The first part should have Content-Type: application/pgp-encrypted instead.
Also before you run into interoperability problems with GnuPG, be aware that latest GnuPG versions are incompatible with OpenPGP standard. This is already causing problems for users of rPGP, the library that Delta Chat uses: Can't decrypt GPG encrypted file · Issue #459 · rpgp/rpgp · GitHub