Files
mailsender/README.md
T
Claude 3bce057eab Add encryption policy, split delivery and SecureMailSender
- delivery: PolicyResolver (address > *@domain > default), RecipientKeyResolver
  (discovery + validation), DeliveryPlanner (REQUIRE refusal, SPLIT/FAIL,
  own copy per Bcc with hidden key IDs), MessageAssembler with sender keys
  for readable sent copies, EnvelopeMimeMessage for per-copy SMTP envelopes
- sender: SecureMailSender with BUILT_IN and RELAY implementations and a
  per-recipient SendReport; relay passes the strictest policy as a header hint
- autoconfigure: mode, default-policy, mixed-mode, recipients, sender-keys
  and relay properties; relay sender is not a bean so spring.mail stays intact
- demo: sends through SecureMailSender

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01G71TQ5jDETVwdUJxGAwaiT
2026-10-12 03:27:40 +00:00

2.8 KiB

mailsender

OpenPGP encryption for Spring Boot mail: discovers recipients' keys via Web Key Directory (WKD) and sends PGP/MIME (RFC 3156), or hands mail to an encrypting relay. See docs/PLAN.md for the design and milestone status.

Requires JDK 27 and Maven 3.9+. Built on Spring Boot 4.1.1 and PGPainless 2.0.

mvn verify

Modules

Module Contents
mailsender-pgp-core WKD key discovery, caching, pinned keys, certificate validation, OpenPGP encryption, PGP/MIME (no Spring)
mailsender-pgp-spring-boot-autoconfigure mail.pgp.* properties and beans
mailsender-pgp-spring-boot-starter dependency bundle for applications
mailsender-demo sample app that sends test mails

Usage

Add pro.thinhha.mailsender:mailsender-pgp-spring-boot-starter, configure spring.mail.* as usual, and send through SecureMailSender instead of JavaMailSender:

SendReport report = secureMailSender.send(message);   // MimeMessage or SimpleMailMessage

Each recipient's key is looked up via WKD (cached) and validated. The message is then sent PGP/MIME-encrypted to recipients with a usable key and in plaintext to the rest (logged as a downgrade), or refused with EncryptionPolicyException if a REQUIRE recipient has no key. SendReport lists the outcome per recipient.

mail:
  pgp:
    mode: BUILT_IN                 # or RELAY: hand plaintext to an encrypting gateway (mail.pgp.relay.*)
    default-policy: OPPORTUNISTIC  # REQUIRE | OPPORTUNISTIC | PLAINTEXT
    mixed-mode: SPLIT              # or FAIL
    recipients:
      "[*@partner.example]": REQUIRE
    sender-keys:                   # your own PUBLIC key, so sent copies stay readable
      "[noreply@example.com]": file:/etc/mail/noreply.pub.asc

Map keys that are addresses need the [...] brackets. See docs/PLAN.md for all options.

Demo app

The demo app sends DEMO_COUNT test mails (default: 1 to test@thinhha.pro) through SecureMailSender and logs the outcome per recipient: PGP/MIME-encrypted (RFC 3156) when a valid WKD key is found, otherwise plaintext. Set MAIL_PGP_MODE=RELAY and RELAY_HOST/RELAY_PORT/RELAY_USERNAME/RELAY_PASSWORD to send via a gateway.

mvn -q install -DskipTests
SMTP_HOST=smtp.example.com SMTP_PORT=587 \
SMTP_USERNAME=me@example.com SMTP_PASSWORD='app-password' \
DEMO_TO=test@thinhha.pro DEMO_COUNT=1 \
java -jar mailsender-demo/target/mailsender-demo-0.1.0-SNAPSHOT.jar
Variable Default Meaning
SMTP_HOST / SMTP_PORT localhost / 587 SMTP server
SMTP_USERNAME / SMTP_PASSWORD empty SMTP credentials
SMTP_AUTH / SMTP_STARTTLS true / true set to false for a local test server
DEMO_TO test@thinhha.pro recipient
DEMO_FROM SMTP_USERNAME sender address
DEMO_COUNT 1 number of mails