Symfony has supported signing and encrypting email messages with the S/MIME standard since Symfony 4.4, and Symfony 7.3 added global signing and encryption so you can configure it once for all your emails. However, S/MIME relies on X.509 certificates issued by certificate authorities, while many people use OpenPGP keys instead. Symfony 8.2 adds support for them and also improves the existing S/MIME features.
PGP/MIME Signing and Encryption
Symfony 8.2 adds PGP/MIME (RFC 3156) support to the Mime component. It works like the S/MIME feature: a signer adds a detached signature to the message and an encrypter encrypts it with the public keys of the recipients:
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\Mime\Crypto\PgpEncrypter;
use Symfony\Component\Mime\Crypto\PgpSigner;
$signer = new PgpSigner('/path/to/secret.asc', '/path/to/public.asc', $passphrase);
$signedEmail = $signer->sign($email);
$encrypter = new PgpEncrypter();
$encryptedEmail = $encrypter->encrypt($signedEmail, [
'alice@example.com' => '/path/to/alice.asc',
]);
In full-stack applications, configure it once in the Mailer settings:
1 2 3 4 5 6 7 8 9 10 11 12
# config/packages/mailer.yaml
framework:
mailer:
pgp_signer:
secret_key: '%kernel.project_dir%/config/keys/private.asc'
passphrase: '%env(PGP_PASSPHRASE)%'
pgp_encrypter:
keys:
'alice@example.com': '%kernel.project_dir%/config/keys/alice.asc'
# or use a service implementing PgpPublicKeyRepositoryInterface
# repository: App\Mailer\PublicKeyRepository
on_missing_key: 'fail' # fail | encrypt | skip
Then, add the X-Pgp-Sign and/or X-Pgp-Encrypt headers to the emails you
want to protect. Symfony signs and encrypts them after rendering their contents
(so it works with TemplatedEmail) and always signs before encrypting:
1 2
$email->getHeaders()->addTextHeader('X-Pgp-Sign', 'true');
$email->getHeaders()->addTextHeader('X-Pgp-Encrypt', 'true');
The on_missing_key option defines what happens when some recipients don't have
a public key: throw an exception, encrypt anyway (they won't be able to read it)
or remove them from the recipients. In any case, the message is never sent
unencrypted. Recipients in Bcc are also hidden inside the encrypted message.
This feature uses the gpg binary via the Process component, so you don't need
any PHP extension. Keep in mind that only the body is encrypted: headers like
Subject, From and To remain visible. The feature is experimental,
so its API may change in minor versions.
Safer S/MIME Encryption
While building PGP/MIME support, some ideas were also applied to the existing S/MIME features. The most important one: until now, if a single recipient had no S/MIME certificate, the message was sent unencrypted to everyone.
Symfony 8.2 adds the on_missing_certificate option with the same values as
the PGP one (fail, encrypt and skip). You can also define the
certificates directly in the configuration:
1 2 3 4 5 6 7
# config/packages/mailer.yaml
framework:
mailer:
smime_encrypter:
certificates:
'alice@example.com': '%kernel.project_dir%/config/certificates/alice.crt'
on_missing_certificate: 'fail'
To keep backward compatibility, the default value is still send_unencrypted,
but it's deprecated and Symfony 9.0 will throw an exception in that case. You can
also override this behavior per message with the X-SMime-Encrypt header
(e.g. X-SMime-Encrypt: skip).
Finally, the DKIM and S/MIME listeners now run in a fixed order (sign, then encrypt, then DKIM) so messages that are both signed and encrypted always work correctly.