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
This commit is contained in:
Claude committed 2026-10-12 03:27:40 +00:00
1 parent a788f7a48a
commit 3bce057eab
34 files changed
+1562 -114

No files matched your search

+11 -6
View File
@@ -117,8 +117,9 @@ mail.pgp:
```
- `RelaySecureMailSender` uses its own `JavaMailSender` pointed at the relay (the app's normal
`spring.mail.*` sender is untouched).
- REQUIRE/OPPORTUNISTIC cannot be enforced by the library in this mode; they are passed as a header hint
and enforcement is the gateway's responsibility. Documented and logged at startup.
- REQUIRE/OPPORTUNISTIC cannot be enforced by the library in this mode. With `policy-header` set, the strictest
policy among the recipients (`require` > `opportunistic` > `plaintext`) is passed as a header hint; enforcement is
the gateway's responsibility. Logged at startup.
- Encrypt-to-self is the gateway's responsibility in this mode.
## Configuration (full shape)
@@ -168,10 +169,14 @@ mail.pgp:
PGPainless and Bouncy Castle can disagree on a shortened expiry; the validator uses the earlier date.
4. **Encryptor + PGP/MIME** — round-trip decrypt test, structure test, CRLF canonicalization, attachments. ✅
Verified live: the demo's mail to `test@thinhha.pro` is addressed to its cv25519 subkey `7CEE00AF05A08313`.
5. **Policy + delivery planner** — table-driven tests over key/no-key × REQUIRE/OPPORTUNISTIC/PLAINTEXT × To/Cc/Bcc;
`EnvelopeMimeMessage`.
6. **SecureMailSender (BUILT_IN + RELAY)**, `SendReport`, sender keys, metrics, autoconfig switch.
5. **Policy + delivery planner** — `PolicyResolver`, `RecipientKeyResolver`, `DeliveryPlanner`, `MessageAssembler`,
`EnvelopeMimeMessage`; tests over key/no-key × REQUIRE/OPPORTUNISTIC/PLAINTEXT × To/Cc/Bcc. ✅
All copies of one message share its Date and Message-ID; duplicates get one copy; nobody encryptable → one
plaintext copy (no split).
6. **SecureMailSender (BUILT_IN + RELAY)**, `SendReport`, sender keys, autoconfig switch. ✅
GreenMail integration test checks per-mailbox (envelope) delivery, decryption by recipient and sender, REQUIRE
refusal with nothing sent, and relay mode with the policy header. Metrics moved to M8.
7. **Demo + E2E** with GreenMail; manual check against a real WKD domain, decrypt in Thunderbird.
Demo app ✅ — encrypts when a valid key is found, otherwise sends plaintext (logged). Verified live:
`test@thinhha.pro` → found via advanced method, fpr `ADA6EFA0CC1630D602889820112495670FF1B4F0`.
8. **Hardening** — protected headers, actuator endpoint, docs.
8. **Hardening** — Micrometer metrics, protected headers, actuator endpoint, docs.