jjoek / laravel-hybrid-encryption
Laravel package for hybrid encryption (RSA-OAEP + AES-256-GCM) for secure API request handling
Package info
github.com/jjoek/laravel-hybrid-encryption
pkg:composer/jjoek/laravel-hybrid-encryption
v1.1.0
2026-04-02 20:21 UTC
Requires
- php: ^8.1
- ext-openssl: *
- illuminate/http: ^10.0|^11.0|^12.0
- illuminate/support: ^10.0|^11.0|^12.0
- phpseclib/phpseclib: ^3.0
Requires (Dev)
- orchestra/testbench: ^8.0|^9.0|^10.0
- phpunit/phpunit: ^10.0|^11.0
README
A Laravel package for hybrid encryption using RSA-OAEP + AES-256-GCM for secure API request handling.
Features
- Hybrid Encryption: Combines RSA-OAEP for key exchange with AES-256-GCM for data encryption
- Automatic Request Decryption: Middleware automatically decrypts encrypted requests
- Public Key Endpoint: Built-in endpoint to expose your public key to frontend clients
- Secure by Default: Uses industry-standard encryption algorithms
Installation
From Packagist
composer require jjoek/laravel-hybrid-encryption
Configuration
1. Generate RSA Key Pair
# Generate private key (2048 or 4096 bits) openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048 # Extract public key openssl rsa -pubout -in private_key.pem -out public_key.pem
2. Add Keys to Environment
Add to your .env file (replace newlines with \n):
ENCRYPTION_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQIBADANBg...\n-----END PRIVATE KEY-----" ENCRYPTION_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkq...\n-----END PUBLIC KEY-----"
Tip: Use this command to format your key for .env:
cat private_key.pem | tr '\n' '\\' | sed 's/\\/\\n/g'
3. Publish Configuration (Optional)
php artisan vendor:publish --tag=hybrid-encryption-config
Usage
Public Key Endpoint
The package automatically registers a public key endpoint:
GET /api/v1/public-key
Response:
{
"publicKey": "-----BEGIN PUBLIC KEY-----\n...",
"algorithm": "RSA-OAEP+AES-GCM-256",
"keyFormat": "PEM"
}
Decrypting Requests
Add the middleware to routes that should accept encrypted requests:
// routes/api.php Route::post('/endpoint', ServiceController::class) ->middleware('decrypt.request');
Expected Request Format
Frontend sends encrypted data with these headers:
X-Encrypted: true
X-Encryption-Algorithm: RSA-OAEP+AES-GCM-256
Request body:
{
"encryptedKey": "<base64-encoded RSA-encrypted AES key>",
"encryptedData": "<base64-encoded AES-GCM encrypted JSON payload>",
"iv": "<base64-encoded 12-byte IV>"
}
Using the Facade
use Jjoek\HybridEncryption\Facades\HybridEncryption; // Get public key $publicKey = HybridEncryption::getPublicKey(); // Check if encryption is configured if (HybridEncryption::isConfigured()) { // Manually decrypt data $decrypted = HybridEncryption::decrypt($encryptedPayload); }
Configuration Options
// config/hybrid-encryption.php return [ // RSA private key (PEM format) 'private_key' => env('ENCRYPTION_PRIVATE_KEY'), // RSA public key (PEM format) 'public_key' => env('ENCRYPTION_PUBLIC_KEY'), // Route configuration 'route' => [ 'enabled' => true, // Enable/disable the public key route 'prefix' => 'api/v1', // Route prefix 'path' => 'public-key', // Route path 'middleware' => ['api'], // Applied middleware 'name' => 'hybrid-encryption.public-key', ], // Middleware alias name 'middleware_alias' => 'decrypt.request', ];
Frontend Implementation (JavaScript)
async function encryptPayload(data, publicKeyPem) { // Import the public key const publicKey = await crypto.subtle.importKey( 'spki', pemToArrayBuffer(publicKeyPem), { name: 'RSA-OAEP', hash: 'SHA-256' }, false, ['encrypt'] ); // Generate random AES key and IV const aesKey = await crypto.subtle.generateKey( { name: 'AES-GCM', length: 256 }, true, ['encrypt'] ); const iv = crypto.getRandomValues(new Uint8Array(12)); // Encrypt the data with AES-GCM const encodedData = new TextEncoder().encode(JSON.stringify(data)); const encryptedData = await crypto.subtle.encrypt( { name: 'AES-GCM', iv }, aesKey, encodedData ); // Encrypt the AES key with RSA-OAEP const rawAesKey = await crypto.subtle.exportKey('raw', aesKey); const encryptedKey = await crypto.subtle.encrypt( { name: 'RSA-OAEP' }, publicKey, rawAesKey ); return { encryptedKey: arrayBufferToBase64(encryptedKey), encryptedData: arrayBufferToBase64(encryptedData), iv: arrayBufferToBase64(iv) }; }
Security Considerations
- Never expose the private key - Keep it secure in environment variables or a secrets manager
- Use HTTPS - Always use TLS in production to protect the encrypted payload in transit
- Key Rotation - Implement a key rotation strategy for production environments
- Key Size - Use at least 2048-bit RSA keys; 4096-bit recommended for sensitive applications
License
MIT License