Working with OpenPGP |
This topic contains the following sections:
Before you can start working with OpenPGP in MimeKit, you will first need to create and register your own OpenPgpContext. For the sake of simplicity, MimeKit includes a GnuPGContext that does most of the work of interoperating with the popular GnuPG program for you.
public class MyGnuPGContext : GnuPGContext { public MyGnuPGContext () { } protected override string GetPasswordForKey (PgpSecretKey key) { // prompt the user (or a secure password cache) for the password for the specified secret key. return "password"; } }
To register your class, you can use the following code snippet:
// Note: by registering our custom context it becomes the default OpenPGP context // instantiated by MimeKit when methods such as Encrypt(), Decrypt(), Sign(), and // Verify() are used without an explicit context. CryptographyContext.Register (typeof (MyGnuPGContext));
Now you are ready to encrypt, decrypt, sign and verify messages using PGP!
PGP/MIME uses a MIME part with a multipart/encrypted mime-type to encapsulate encrypted data. To encrypt any MimeEntity, use the MultipartEncrypted.Encrypt method:
public void Encrypt (MimeMessage message) { // encrypt our message body using our custom GnuPG cryptography context using (var ctx = new MyGnuPGContext ()) { // Note: this assumes that each of the recipients has a PGP key associated // with their email address in the user's public keyring. // // If this is not the case, you can use SecureMailboxAddresses instead of // normal MailboxAddresses which would allow you to specify the fingerprint // of their PGP keys. You could also choose to use one of the Encrypt() // overloads that take a list of PgpPublicKeys. message.Body = MultipartEncrypted.Encrypt (ctx, message.To.Mailboxes, message.Body); } }
Tip |
---|
When you know that you will be encrypting a message, it may be a good idea to use a SecureMailboxAddress instead of a MailboxAddress for each of the recipients, allowing you to specify the unique fingerprint of each recipient's PGP key. |
As mentioned earlier, PGP/MIME uses a multipart/encrypted part to encapsulate the encrypted content.
A multipart/encrypted contains exactly 2 parts: the first MimeEntity is the version information while the second MimeEntity is the actual encrypted content and will typically be an application/octet-stream.
The first thing you must do is find the MultipartEncrypted part (see the section on Working with messages).
public MimeEntity Decrypt (MimeMessage message) { if (message.Body is MultipartEncrypted) { // the top-level MIME part of the message is encrypted using PGP/MIME var encrypted = (MultipartEncrypted) entity; return encrypted.Decrypt (); } else { // the top-level MIME part is not encrypted return message.Body; } }
PGP/MIME uses a MIME part with a multipart/signed mime-type to contain the signed content and the detached signature data.
Here's how you might digitally sign a message using PGP/MIME:
public void Sign (MimeMessage message) { // digitally sign our message body using our custom GnuPG cryptography context using (var ctx = new MyGnuPGContext ()) { // Note: this assumes that the Sender address has an S/MIME signing certificate // and private key with an X.509 Subject Email identifier that matches the // sender's email address. // // If this is not the case, you can use a SecureMailboxAddress instead of a // normal MailboxAddress which would allow you to specify the fingerprint // of the sender's private PGP key. You could also choose to use one of the // Create() overloads that take a PgpSecretKey, instead. var sender = message.From.Mailboxes.FirstOrDefault (); message.Body = MultipartSigned.Create (ctx, sender, DigestAlgorithm.Sha1, message.Body); } }
You can also do your own PGP key lookups instead of relying on email addresses to match up with the user's secret key.
public void Sign (MimeMessage message, PgpSecretKey key) { // digitally sign our message body using our custom GnuPG cryptography context using (var ctx = new MyGnuPGContext ()) { message.Body = MultipartSigned.Create (ctx, key, DigestAlgorithm.Sha1, message.Body); } }
As mentioned earlier, PGP/MIME uses a multipart/signed part to contain the signed content and the detached signature data.
A multipart/signed contains exactly 2 parts: the first MimeEntity is the signed content while the second MimeEntity is the detached signature and, by default, will be an ApplicationPgpSignature part.
Because the multipart/signed part may have been signed by multiple signers, it is important to verify each of the digital signatures (one for each signer) that are returned by the Verify method:
public void Verify (MimeMessage message) { if (message.Body is MultipartSigned) { var signed = (MultipartSigned) message.Body; foreach (var signature in signed.Verify ()) { try { bool valid = signature.Verify (); // If valid is true, then it signifies that the signed content // has not been modified since this particular signer signed the // content. // // However, if it is false, then it indicates that the signed // content has been modified. } catch (DigitalSignatureVerifyException) { // There was an error verifying the signature. } } } }