1%
1 words - 1 min read.

I recently had the need to send signed, encrypted email via Mailgun for a project. Unfortunately the documentation for signing and sending pgp/mime emails via Mailgun is pretty sparse, so I thought I'd post about it here for anyone else who comes across this problem.

The first step is to select a mime builder library to construct your pgp/mime payload. I chose pgpbuilder due to it's simplicity.

The second step is to utilize the mailgun-js library, specifically the sendMime() function. The regular send() function hits the /messages Mailgun API endpoint rather than the /messages.mime API endpoint.

That's it! Below is some code that might help you get started, along with a quick snippet on generating a private key to sign the emails you send. You'll also need to have access to your recipients public keys as well in order to encrypt email for them.

const PgpBuilder = require('pgpbuilder');
const Mailgun = require('mailgun-js');

const publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBFkwtpgBEAC1tg257id+ZiSfDLkwhO1pVb+DsnBPcFOu71S5D41CZnKoWrJr
8Ot7tvLQQ1FQAsF9BORW1Y9c49YtXyZpHnvke/t+mCAc1BFUltOBqBd6PLaR+mSY
....
-----END PGP PUBLIC KEY BLOCK-----`;

const privateKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----

lQPGBFr8NQgBCACm6BObMcpwn8G2J1N2rsDqsKWvhDFNMczrI5GwwKzhAjg6F2T5
JCJKBMPSVihwNNxJhwUopd4qNgXWgJRjZawT4e2Tu1nl2nY9I8j9+jHK78xLc0b4
...
-----END PGP PRIVATE KEY BLOCK-----`;


(async () => {
    const mailgun = new Mailgun({
        apiKey: "key-*****",
        domain: "*****.com"
    });
    const pgpbuilder = new PgpBuilder(undefined, undefined);
    // setPrivateKey() signs the email with the given private key
    pgpbuilder.setPrivateKey({
        privateKeyArmored: privateKey,
        passphrase: '', // if private key has a passphrase, enter it here
    });
    const res = await pgpbuilder.encrypt({
        mail: {
            from: [{ address: "foo at bar.com" }], // replace at with @
            to: ["bar at foo.com"], // replace at with @
            subject: "this is a secret message",
            body: "the empire did nothing wrong",
            attachments: [{
                mimeType: 'text/csv',
                filename: 'test.csv',
                content: 'abc,123,456'
            }],
        },
        publicKeysArmored: [ publicKey ],
    });

    const { rfcMessage } = await pgpbuilder.buildEncrypted({ mail: res });
    await mailgun.messages().sendMime({
        to,
        message: rfcMessage.toString('ascii'),
    });
})()

The 'to' array of email addresses must match the order of the 'publicKeysArmored' array. The 'publicKeysArmored' array contains only raw public keys in string form.

Creating the ascii armored private key for your project

To create a valid private key for signing emails use gpg:

$ gpg --generate-key
gpg (GnuPG/MacGPG2) 2.2.3; Copyright (C) 2017 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Note: Use "gpg --full-generate-key" for a full featured key generation dialog.

GnuPG needs to construct a user ID to identify your key.

Real name: Turd Ferguson
Email address: foo at bar.com
You selected this USER-ID:
    "Turd Ferguson foo at bar.com"

Change (N)ame, (E)mail, or (O)kay/(Q)uit? O

Once you've created the key, export it:

gpg --export-secret-key foo at bar.com > private_key_sender.asc

Once the *.asc file has been created, open it and extract the private key portion and use that string when calling setPrivateKey() on the pgpbuilder object. Setting the private key signs the email to verify that the email was actually sent by you.

© 2018. All Rights Reserved.

Proudly published with Ghost