spatie/crypto
Generate RSA key pairs and encrypt/decrypt (and sign/verify) data using private/public keys in PHP. Provides simple wrappers around OpenSSL for better DX, with support for loading keys from files and writing generated keys to disk.
Installation:
composer require spatie/crypto
Generate Key Pair:
use Spatie\Crypto\Rsa\KeyPair;
[$privateKey, $publicKey] = (new KeyPair())->generate();
(new KeyPair())->generate(storage_path('app/private_key.pem'), storage_path('app/public_key.pem'));
First Use Case: Encrypt/Decrypt Data
use Spatie\Crypto\Rsa\PrivateKey;
use Spatie\Crypto\Rsa\PublicKey;
$privateKey = PrivateKey::fromFile(storage_path('app/private_key.pem'));
$publicKey = PublicKey::fromFile(storage_path('app/public_key.pem'));
$data = 'Sensitive API token';
$encrypted = $privateKey->encrypt($data); // Encrypt with private key (for signing)
$decrypted = $publicKey->decrypt($encrypted); // Decrypt with public key
KeyPair class: For generating and managing key pairs.PrivateKey and PublicKey classes: Core methods for encryption/decryption and signing/verification.openssl_* wrappers: Understand the underlying OpenSSL functions (e.g., openssl_private_encrypt, openssl_public_decrypt).$keyPair = (new KeyPair())->generate(
storage_path('app/keys/private.pem'),
storage_path('app/keys/public.pem')
);
$privateKey = PrivateKey::fromFile(storage_path('app/keys/private.pem'));
$publicKey = PublicKey::fromFile(storage_path('app/keys/public.pem'));
.env to define paths:
PRIVATE_KEY_PATH=storage/app/keys/private.pem
PUBLIC_KEY_PATH=storage/app/keys/public.pem
$privateKey = PrivateKey::fromFile(config('crypto.private_key_path'));
$signature = $privateKey->encrypt('data_to_sign');
$verifiedData = $publicKey->decrypt($signature);
$encryptedData = $publicKey->encrypt('sensitive_data');
$decryptedData = $privateKey->decrypt($encryptedData);
$signature = $privateKey->sign('data_to_sign');
$isValid = $publicKey->verify('data_to_sign', $signature);
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(PrivateKey::class, function () {
return PrivateKey::fromFile(config('crypto.private_key_path'));
});
$this->app->singleton(PublicKey::class, function () {
return PublicKey::fromFile(config('crypto.public_key_path'));
});
}
public function handle($request, Closure $next)
{
$publicKey = app(PublicKey::class);
if (!$publicKey->verify($request->data, $request->signature)) {
abort(403, 'Invalid signature');
}
return $next($request);
}
$user = User::create([
'name' => 'John Doe',
'encrypted_token' => $privateKey->encrypt($request->api_token),
]);
$token = $user->encrypted_token ? $publicKey->decrypt($user->encrypted_token) : null;
Key File Permissions:
www-data or nginx).chmod 600 storage/app/keys/private.pem
chown www-data:www-data storage/app/keys/private.pem
Key Rotation:
$oldPrivateKey = PrivateKey::fromFile('old_private.pem');
$newPrivateKey = PrivateKey::fromFile('new_private.pem');
$newPublicKey = PublicKey::fromFile('new_public.pem');
User::whereNotNull('encrypted_token')->each(function ($user) use ($oldPrivateKey, $newPrivateKey, $newPublicKey) {
$decrypted = $oldPrivateKey->decrypt($user->encrypted_token);
$user->encrypted_token = $newPrivateKey->encrypt($decrypted);
$user->save();
});
Data Size Limits:
openssl_error_string exception.$aesKey = openssl_random_pseudo_bytes(32);
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encryptedData = openssl_encrypt('sensitive_data', 'aes-256-cbc', $aesKey, OPENSSL_RAW_DATA, $iv);
$encryptedAesKey = $publicKey->encrypt($aesKey);
Key Format:
openssl rsa -in key.der -inform DER -out key.pem -outform PEM
OpenSSL Extensions:
Class 'Spatie\Crypto\Rsa\KeyPair' not found or openssl_* function errors.php.ini:
extension=openssl
Check OpenSSL Errors:
try {
$decrypted = $publicKey->decrypt($encryptedData);
} catch (\Exception $e) {
\Log::error('OpenSSL Error: ' . openssl_error_string());
throw $e;
}
Validate Key Pairs:
if (!$privateKey->isValid() || !$publicKey->isValid()) {
throw new \RuntimeException('Invalid key pair');
}
Test with Known Values:
$testData = 'test123';
$encrypted = $privateKey->encrypt($testData);
$decrypted = $publicKey->decrypt($encrypted);
assert($testData === $decrypted, 'Key pair test failed');
Use Config Files:
config/crypto.php:
return [
'private_key_path' => storage_path('app/keys/private.pem'),
'public_key_path' => storage_path('app/keys/public.pem'),
];
Environment-Specific Keys:
.env to switch keys between environments:
CRYPTO_PRIVATE_KEY=storage/app/keys/local_private.pem
CRYPTO_PUBLIC_KEY=storage/app/keys/local
How can I help you explore Laravel packages today?