PGP-signing multipart e-mails with Python

The problem is that Python's email.generator module doesn't add a newline before the signature part. I've reported that upstream as http://bugs.python.org/issue14983.

(The bug was fixed in Python2.7 and 3.3+ in 2014)


What is actually the MIME structure of basemsg? It appears that it has too many nested parts in it. If you export a signed message from e.g. Evolution, you'll see that it has just two parts: the body and the signature.

Here's an example which generates a message on stdout that can be read and the signature verified on both mutt (mutt -f test.mbox) and Evolution (File -> Import).

import gnupg
from email.message import Message
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

body = """
This is the original message text.

:)
"""

gpg_passphrase = "xxxx"

basemsg = MIMEText(body)

def messageFromSignature(signature):
    message = Message()
    message['Content-Type'] = 'application/pgp-signature; name="signature.asc"'
    message['Content-Description'] = 'OpenPGP digital signature'
    message.set_payload(signature)
    return message

gpg = gnupg.GPG()
basetext = basemsg.as_string().replace('\n', '\r\n')
signature = str(gpg.sign(basetext, detach=True, passphrase=gpg_passphrase))
if signature:
    signmsg = messageFromSignature(signature)
    msg = MIMEMultipart(_subtype="signed", micalg="pgp-sha1",
    protocol="application/pgp-signature")
    msg.attach(basemsg)
    msg.attach(signmsg)
    msg['Subject'] = "Test message"
    msg['From'] = "[email protected]"
    msg['To'] = "[email protected]"
    print(msg.as_string(unixfrom=True)) # or send
else:
    print('Warning: failed to sign the message!')

Note that here, I'm assuming a keyring with a passphrase, but you may not need that.