Click or drag to resize
MimeKit

Working with OpenPGP

This topic contains the following sections:

Creating your own OpenPGP Context

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.

C#
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:

C#
// 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!

Encrypting Messages with PGP/MIME

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:

C#
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  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.

Decrypting PGP/MIME Messages

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).

C#
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;
    }
}
Digitally Signing Messages using PGP/MIME

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:

C#
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.

C#
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);
    }
}
Verifying PGP/MIME Digital Signatures

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:

C#
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.
            }
        }
    }
}
See Also