A Symfony bundle to interact with PHP's Sodium extension.
To use this package, require it in your Symfony project with Composer.
composer require geekcell/sodium-bundle
Verify that the bundle has been enabled in config/bundles.php
<?php
return [
// other bundles ...
GeekCell\SodiumBundle\GeekCellSodiumBundle::class => ['all' => true],
];
At this point in time, this bundle only supports libsodium's anonymous and authenticated public-key encryption.
Create a config file config/packages/geek_cell_sodium.yaml
where you configure your base64-encoded public and private/secret keys for encryption and decryption. It is very strongly recommended to not store them as plain text, but read them from your .env.local
, which is added to your .gitignore
file.
geek_cell_sodium:
public_key: '%env(SODIUM_PUBLIC_KEY)%'
private_key: '%env(SODIUM_PRIVATE_KEY)%'
Only the public_key
field is mandatory, if you only plan for anonymous (shared) public-key encryption in your app. For both authenticated and anonymous decryption, a private_key
must also be configured, or an exception is thrown during runtime.
This bundle ships with a console command sodium:generate-keys
to generate a set of public/private keys for you.
❯ bin/console sodium:generate-keys
Generating a new set of public and private keys...
Public Key: cqJZXt1dhZtyYZ0NcOmwkgcyvW2t9w2Wdwe/Wk6zegk=
Private Key: G3XKnSunNpN1LHKY34LFen7XI2dmu6xBk9UeTQIxNwY=
Please add or update the following environment variables in your .env.local file:
SODIUM_PUBLIC_KEY=cqJZXt1dhZtyYZ0NcOmwkgcyvW2t9w2Wdwe/Wk6zegk=
SODIUM_PRIVATE_KEY=G3XKnSunNpN1LHKY34LFen7XI2dmu6xBk9UeTQIxNwY=
Done!
Simply typehint the GeekCell\SodiumBundle\Sodium\Sodium
service in your code and make use of its encrypt
and decrypt
methods:
The example below demonstrates anonymous encryption using only a shared public key. In order to decrypt a message, the receiver needs both public and corresponding private/secret key.
<?php
// Sender
namespace Alice\Service;
use GeekCell\SodiumBundle\Sodium\Sodium;
class AnonymousEncryptionService
{
public function __construct(
private readonly Sodium $sodium,
) {}
public function encryptMessage(string $message): string
{
return $this->sodium
->with('box')
->encrypt($message)
;
}
}
<?php
// Receiver
namespace Bob\Service;
use GeekCell\SodiumBundle\Sodium\Sodium;
class AnonymousDecryptionService
{
public function __construct(
private readonly Sodium $sodium,
) {}
public function decryptMessage(string $message): string
{
return $this->sodium
->with('box')
->decrypt($message)
;
}
}
Alternatively you can use authenticated public-key encyption to encrypt specifically encrypt messages by using a recipient's public key and a nonce. When received, the recipient can then decrypt a cipher using the sender's public key and nonce.
// Sender
namespace Alice\Service;
use GeekCell\SodiumBundle\Sodium\Sodium;
class AuthenticatedEncryptionService
{
public function __construct(
private readonly Sodium $sodium,
) {}
public function encryptMessage(string $message, string $recipientPublicKey, $string $nonce): string
{
return $this->sodium
->with('box')
->for($recipientPublicKey)
->encrypt($message, $nonce)
;
}
}
<?php
// Receiver
namespace Bob\Service;
use GeekCell\SodiumBundle\Sodium\Sodium;
class AuthenticatedDecryptionService
{
public function __construct(
private readonly Sodium $sodium,
) {}
public function decryptMessage(string $message, string $senderPublicKey, string $nonce): string
{
return $this->sodium
->with('box')
->from($senderPublicKey)
->decrypt($message, $nonce)
;
}
}
For situations where you cannot inject GeekCell\SodiumBundle\Sodium\Sodium
via Symfony's DIC (for example if you want to directly encrypt or decrypt fields of your Doctine entity), you can use a container-facade for your convenience:
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use GeekCell\SodiumBundle\Support\Facade\Sodium;
#[ORM\Entity]
class DiaryEntry
{
#[ORM\Id]
#[ORM\GeneratedValue]
private ?int $id;
#[ORM\Column(type: 'datetime')]
private \DateTimeInterface $date;
#[Column(type: 'text')]
private string $encryptedEntry;
public function setEntry(string $entry): void
{
$this->encryptedEntry = Sodium::with('box')->encrypt($entry);
}
// ...
}
For more information, check out geekcell/container-facade.