# 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](docs/PLAN.md) for the design and milestone status. Requires JDK 25 or newer; the Maven Wrapper (`./mvnw`) downloads Maven 3.9.11. Built on Spring Boot 4.1.1 and PGPainless 2.0. ``` ./mvnw 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 | ## Installation Releases and snapshots are published to the Gitea Maven registry of the `public` owner by `.gitea/workflows/publish.yml` (push to `main` → `-SNAPSHOT`, tag `v1.2.3` → `1.2.3`): ```xml gitea-public https://YOUR-GITEA-HOST/api/packages/public/maven pro.thinhha.mailsender mailsender-pgp-spring-boot-starter 0.1.0 ``` If the packages are not public, add a `` with id `gitea-public` and a token with `read:package` scope to your `settings.xml`. ## Usage Add `pro.thinhha.mailsender:mailsender-pgp-spring-boot-starter`, configure `spring.mail.*` as usual, and send through `SecureMailSender` instead of `JavaMailSender`: ```java 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. ```yaml 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](docs/PLAN.md) for all options. - `mail.pgp.protected-headers=true` encrypts the real Subject too (RFC 9788); the visible Subject becomes `[...]`. - With Micrometer, `mail.pgp.recipients{outcome,policy,reason}`, `mail.pgp.refused`, `mail.pgp.key.lookup` and `mail.pgp.key.cache.size` are recorded. - With Actuator, expose `pgpkeys` to inspect an address (`GET /actuator/pgpkeys/{email}`) or evict cached keys (`DELETE /actuator/pgpkeys/{email}`). ## 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. ``` ./mvnw -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 |