S/MIME Part 4: Collecting Recipient Public Keys the Easy Way – with SparkPost Inbound Relay Webhooks
Bird
Feb 1, 2019
1 min read

Key Takeaways
Premise: Sending S/MIME-encrypted email isn’t hard once you can automatically collect each recipient’s public key. This post closes that gap by using SparkPost Inbound Relay Webhooks to receive signed emails, extract certificates, and store them for later encryption.
Goal: Build a Flask-based webhook service that listens for incoming signed messages, validates them (DKIM + certificate checks), and safely writes each public key to disk for use in outbound secure mail.
Highlights:
Problem: Manual key exchange doesn’t scale for app-generated email.
Solution: Invite users to send a signed email; the inbound webhook automatically parses and stores their PEM certificate.
Setup steps:
Configure an Inbound Domain and MX records (e.g., inbound.yourdomain.com).
Create an Inbound Relay Webhook via the SparkPost API pointing to your app endpoint.
Deploy a small Flask app (webapp.py) using configuration from webapp.ini.
Log extensively for transparency; integrate Pytest + Travis CI for automated validation.
Security measures:
Verify DKIM signatures and message authenticity.
Validate certificate trust chain before storing.
Use a secret auth token in webhook headers.
Output:
Each valid inbound message creates a certificate file such as bob.lumreeker@gmail.com.crt.
Once stored, these keys enable encrypted replies using earlier scripts from parts 2 and 3.
Q&A Highlights
Why is collecting recipient keys so critical for S/MIME?
Because encryption requires each recipient’s public key; automating this step lets any app send secure mail without manual exchange.
How does SparkPost Inbound Relay Webhook simplify key collection?
It converts any signed inbound email into a structured JSON payload, allowing your app to parse and persist certificates programmatically.
What safeguards prevent spoofing or junk submissions?
The service validates DKIM signatures, enforces authentication tokens, and rejects malformed or unsigned messages.
Where are certificates stored and in what format?
They’re written to disk in PEM format (
.crtfiles), ready for use by the signing/encryption toolchain built in previous parts.What’s the developer workflow?
Run the Flask app, verify with Postman using the provided sample payload, then connect it to SparkPost’s live webhook for continuous operation.
Overall takeaway?
S/MIME key management can be fully automated with a few lines of Python and SparkPost APIs—bringing scalable encryption to any app-generated email workflow.
In this series, we’ve seen how including an S/MIME signature is fairly straightforward. Sending S/MIME encrypted mail is more complex because you need to obtain recipient public keys. It’s one thing when you’re using a mail client for humans such as Thunderbird – but how can that work with app-generated email streams?
In part 1, we had a quick tour of S/MIME, looking at signing and encryption of our message streams across a range of mail clients. Part 2 took us through a simple command-line tool to sign and encrypt emails, then send them through SparkPost. Part 3 showed how to inject secure mail streams into on-premises platforms such as Port25 PowerMTA and Momentum.
In this series, we’ve seen how including an S/MIME signature is fairly straightforward. Sending S/MIME encrypted mail is more complex because you need to obtain recipient public keys. It’s one thing when you’re using a mail client for humans such as Thunderbird – but how can that work with app-generated email streams? App-generated emails, like those used by dating platforms, require careful strategy to maximize engagement. See how dating apps create compelling triggered email experiences.
But wait – there is another way into Mordor to get those keys. Your service can invite your customers (via email, of course) to send you back a signed mail to a known customer-service address. Using the magical powers of SparkPost Inbound Relay webhooks, we’ll extract and store that public key for you to use.
We can summarise this in a simple use-case:
As a recipient of messages, I provide your service with my personal email signature via email, so that in future, emails can be sent to me in S/MIME encrypted form.
From this, let’s derive some more detailed requirements:
We need an always-on, reliable inbound email service to receive those signed emails.
There should be no special requirements on the mail format, other than it should carry an S/MIME signature.
Because anyone can try to send a mail to this service, it should be designed defensively, for example, to reject “spoof” messages from bad actors. There will need to be several layers of checking.
If everything checks out OK, the service will store the certificate in a file, using the well-known plain-text Privacy-Enhanced Mail (PEM) format.
There are some non-functional requirements:
Machine-to-machine webhook services can be hard to see just from responses to what’s happening inside. The service should provide extensive human-readable application-level logs. In particular, the certificate parsing and checking should be logged.
We add test cases for the app internals, using the nice Pytest framework, and run those tests automatically on check-in using Travis CI integration with GitHub.
OK – let’s get started!
1. Solution overview
Here’s what the overall solution will look like.

2. Installing, configuring and starting the web app
3. SparkPost inbound relay webhooks setup
Firstly, we select a domain to use as our inbound message address – here, it will be inbound.thetucks.com. Set your domain up following this guide. Here are the steps I used in detail:
3.1 Add MX Records
You’ll need access to your specific Internet Service Provider account. When done, you can check them with dig – here’s the command for my domain.
You should see:
3.2 Create an Inbound Domain
Use the SparkPost Postman API collection, selecting the Inbound Domains / Create .. call. The body of the POST request contains your domain, for example:

3.3 Create a Relay Webhook
Create an inbound relay webhook using the relevant Postman call. The message body in my case contains:
As mentioned before, I recommend setting an auth_token to your own secret value, as set in the webapp.ini file on your host.
Your “target” value needs to match your host address and TCP port where you’ll be hosting the web app.
Your “domain” value needs to match your MX records set up in step 1.

That’s it! The plumbing is done. You should now be able to send certificates to your inbound address, they will be processed and show up on your web application host – in this case, a file named bob.lumreeker@gmail.com.crt.
Now you can send encrypted emails to Bob, using the tools described in parts 2 & 3 of this series.
You can examine the contents of a certificate using:
4. Internals: DKIM checking, certificate validation
The app checks received emails have valid DKIM and checks that the certificates themselves are valid, as described here. There are implementation notes in there too, and ideas for further work.
Summing up…
We’ve seen how recipient public keys can be gathered easily using an email to an inbound relay webhooks address. Once done, those recipients can receive their messages in S/MIME encrypted form.
That’s it for now! Happy sending.



