laravel-hybrid-encryption maintained by aptika
Laravel Hybrid Encryption
Package Laravel untuk mengenkripsi payload menggunakan AES-256-GCM dan RSA-OAEP. AES key dapat berasal dari APP_KEY Laravel atau diberikan oleh aplikasi sebagai AES key generated.
Alur encrypt dan decrypt
Flowchart encrypt
flowchart TD
E0([Mulai encrypt]) --> E1[Input: key_id dan data asli]
E1 --> E2[KeyPairService membaca public.pem berdasarkan key_id]
E1 --> E3{Pilih sumber AES key}
E3 -->|aesKey kosong| E4[Gunakan material 32 byte dari APP_KEY]
E3 -->|aesKey diberikan| E5[Validasi AES key 32 byte]
E1 --> E6[Buat IV acak 12 byte]
E4 --> E7[AES-GCM encrypt]
E5 --> E7
E6 --> E7
E7 --> E8[Hasil: data ciphertext dan authentication tag]
E2 --> E9[RSA-OAEP encrypt AES key]
E4 --> E9
E5 --> E9
E9 --> E10[Hasil: encrypted_key]
E8 --> E11[Gabungkan payload]
E10 --> E11
E6 --> E11
E11 --> E12([Satu string kompak dikirim])
Hasil encrypt tidak menyimpan data asli. Hasilnya adalah satu string kompak berawalan
AHE3.. Di dalamnya tetap terdapat encrypted_key, iv, tag, dan data, tetapi
nama field dan metadata tidak terlihat sebagai JSON.
Flowchart decrypt
flowchart TD
D0([Mulai decrypt]) --> D1[Input: key_id dan payload]
D1 --> D2[KeyPairService membaca private.pem berdasarkan key_id]
D1 --> D3[Bongkar string kompak menjadi encrypted_key, iv, tag, data]
D2 --> D4[RSA-OAEP decrypt]
D3 --> D4
D4 --> D5[AES key dari payload]
D1 --> D6{AES key diberikan?}
D6 -->|Tidak| D7[Gunakan material 32 byte dari APP_KEY]
D6 -->|Ya| D10[Validasi AES key 32 byte]
D5 --> D8{AES key sama?}
D7 --> D8
D8 -->|Tidak| D9([Gagal: APP_KEY berbeda])
D8 -->|Ya| D11[Ambil data, iv, dan tag dari payload]
D10 --> D8
D11 --> D12[AES-GCM decrypt dan validasi tag]
D12 --> D13{Payload valid?}
D13 -->|Tidak| D14([Gagal: payload diubah])
D13 -->|Ya| D15([Data asli dikembalikan])
Urutannya adalah: RSA membuka AES key terlebih dahulu, kemudian AES-GCM membuka data. Jika $aesKey tidak diberikan, plugin menggunakan APP_KEY; jika $aesKey diberikan, plugin menggunakan key tersebut pada encrypt dan decrypt.
Jika APP_KEY berbeda, validasi AES key gagal. Jika RSA private key tidak cocok dengan public key yang digunakan saat encrypt, encrypted_key tidak dapat dibuka.
Struktur penyimpanan key
flowchart TD
D[Disk Laravel: local<br/>Visibility: private]
D --> PUB[hybrid-encryption/keys/public]
D --> PRI[hybrid-encryption/keys/private]
PUB --> P1[integrations/app1/public.pem]
PRI --> P2[integrations/app1/private.pem]
PUB --> P3[users/10/profile/public.pem]
PRI --> P4[users/10/profile/private.pem]
Public dan private key berada pada disk yang sama, tetapi prefix/folder-nya berbeda. Visibility default keduanya adalah private.
Persyaratan
- PHP
^8.2. - Laravel dengan
illuminate/supportversi10,11,12, atau13. - Ekstensi PHP OpenSSL aktif.
- Composer.
Instalasi
Di project Laravel:
composer require aptika/laravel-hybrid-encryption
php artisan vendor:publish --tag=hybrid-encryption-config
php artisan hybrid-encryption:generate-key-pair default
Laravel menemukan service provider dan facade melalui Composer auto-discovery. Jika auto-discovery dinonaktifkan, daftarkan provider berikut secara manual:
Aptika\HybridEncryption\HybridEncryptionServiceProvider::class,
Command tersebut menghasilkan pasangan key pada disk dan prefix yang dikonfigurasi:
storage/app/hybrid-encryption/keys/public/default/public.pem
storage/app/hybrid-encryption/keys/private/default/private.pem
Simpan private.pem hanya di server penerima. Jangan commit atau mengirimkannya ke project lain.
Konfigurasi
File config/hybrid-encryption.php:
return [
'cipher' => 'aes-256-gcm',
'rsa_bits' => (int) env('APTIKA_HYBRID_ENCRYPTION_RSA_BITS', 3072),
'key_storage' => [
'disk' => env('APTIKA_HYBRID_ENCRYPTION_KEY_DISK', 'local'),
'visibility' => env('APTIKA_HYBRID_ENCRYPTION_KEY_VISIBILITY', 'private'),
'public_prefix' => env('APTIKA_HYBRID_ENCRYPTION_PUBLIC_PREFIX', 'hybrid-encryption/keys/public'),
'private_prefix' => env('APTIKA_HYBRID_ENCRYPTION_PRIVATE_PREFIX', 'hybrid-encryption/keys/private'),
],
];
Override lokasi key bila diperlukan:
APTIKA_HYBRID_ENCRYPTION_RSA_BITS=3072
cipher saat ini ditetapkan package sebagai aes-256-gcm. rsa_bits digunakan saat membuat key baru.
Jika AES key tidak diberikan pada method, package menggunakan APP_KEY Laravel. Jika AES key diberikan pada method, package menggunakan AES key tersebut dan tidak menyimpannya.
key_storage digunakan untuk key pair per user atau per fitur. Public dan private key disimpan pada satu disk Laravel yang sama, tetapi foldernya berbeda. Default-nya adalah disk local dengan visibility private:
APTIKA_HYBRID_ENCRYPTION_KEY_DISK=local
APTIKA_HYBRID_ENCRYPTION_KEY_VISIBILITY=private
APTIKA_HYBRID_ENCRYPTION_PUBLIC_PREFIX=hybrid-encryption/keys/public
APTIKA_HYBRID_ENCRYPTION_PRIVATE_PREFIX=hybrid-encryption/keys/private
Visibility private membuat file key tidak ditujukan untuk akses URL publik. Pastikan disk yang digunakan memang tidak diekspos langsung oleh web server.
Pemakaian dasar
use Aptika\HybridEncryption\Facades\HybridEncryption;
$keyId = 'default';
$payload = HybridEncryption::encrypt($keyId, [
'nik' => '750101xxxxxxxxxx',
'nama' => 'Alfandy',
]);
$data = HybridEncryption::decrypt($keyId, $payload);
// ['nik' => '750101xxxxxxxxxx', 'nama' => 'Alfandy']
Untuk AES key hasil generate, berikan key yang sama saat encrypt dan decrypt:
$aesKey = env('FEATURE_BANTUAN_AES_KEY'); // format base64:... atau 32 byte
$payload = HybridEncryption::encrypt(
'features/bantuan',
'alfandy',
$aesKey
);
$data = HybridEncryption::decrypt(
'features/bantuan',
$payload,
$aesKey
);
Dependency injection juga tersedia:
use Aptika\HybridEncryption\Services\HybridEncryptionService;
public function store(HybridEncryptionService $crypto)
{
return response($crypto->encrypt('features/example', ['message' => 'rahasia']))
->header('Content-Type', 'text/plain');
}
encrypt() menerima key_id dan array/string, lalu mengembalikan satu string
kompak yang dapat dikirim langsung melalui HTTP. decrypt() menerima key_id
dan string kompak tersebut, lalu mengembalikan JSON object/array sebagai
associative array; plaintext yang bukan JSON dikembalikan sebagai string.
Contoh mengirim payload sebagai satu string:
$payloadString = $crypto->encrypt('integrations/app1', $data);
Http::withBody($payloadString, 'text/plain')
->post('https://app2.test/api/receive');
Di aplikasi penerima:
$data = $crypto->decrypt(
'integrations/app1',
$request->getContent()
);
Pertukaran data antar project
-
Di project B buat key pair:
php artisan hybrid-encryption:generate-key-pair features/antar-project -
Pastikan A dan B memiliki akses ke public key B melalui disk yang sesuai. Jika memakai AES generated, simpan AES key yang sama pada secret manager kedua aplikasi.
-
Di A gunakan
key_idB:use Aptika\HybridEncryption\Services\HybridEncryptionService; $payload = app(HybridEncryptionService::class)->encrypt( 'features/antar-project', ['nik' => '750101xxxxxxxxxx', 'nama' => 'Alfandy'] ); -
Kirim payload melalui HTTPS ke B.
-
Di B decrypt dengan private key B:
use Aptika\HybridEncryption\Facades\HybridEncryption; use Illuminate\Http\Request; public function receive(Request $request) { return response()->json([ 'success' => true, 'data' => HybridEncryption::decrypt( 'features/antar-project', $request->getContent() ), ]); }
Public key boleh dibagikan kepada pengirim; private key tidak boleh dibagikan.
Key berbeda untuk setiap user atau fitur
Buat key pair menggunakan key_id yang stabil dan aman, misalnya:
php artisan hybrid-encryption:generate-key-pair users/10/profile
php artisan hybrid-encryption:generate-key-pair features/bantuan
php artisan hybrid-encryption:generate-key-pair features/bantuan --aes-source=generated
Tanpa --aes-source, command menanyakan pilihan app_key atau generated. Mode app_key menggunakan APP_KEY Laravel. Jika belum tersedia, command menjalankan php artisan key:generate tanpa menimpa APP_KEY yang sudah ada. Mode generated membuat AES key acak 32 byte, menampilkannya satu kali, dan tidak menyimpan file AES.
Membuat AES key mandiri
Jika hanya membutuhkan AES key baru tanpa membuat RSA key pair baru, jalankan:
php artisan hybrid-encryption:generate-aes-key
Contoh output:
AES-256 key berhasil dibuat.
AES key: base64:...
Fingerprint: ...
Gunakan AES key tersebut saat encrypt dan decrypt:
$aesKey = env('FEATURE_BANTUAN_AES_KEY');
$payload = $crypto->encrypt('features/bantuan', $data, $aesKey);
$result = $crypto->decrypt('features/bantuan', $payload, $aesKey);
Command membuat 32 byte acak dengan random_bytes(32), memformatnya sebagai
base64:..., dan tidak menulis AES key ke disk. Simpan hasilnya di secret
manager atau environment. Jangan menyimpan output tersebut di Git, log,
response API, atau frontend.
Jika key pair sudah ada, command tidak melempar stack trace. Command menampilkan pesan dan berhenti tanpa mengubah key:
Pasangan key untuk key_id [default] sudah ada.
Key lama tidak diubah.
Jika memang ingin mengganti key, jalankan ulang dengan --force.
Untuk mengganti key secara sadar:
php artisan hybrid-encryption:generate-key-pair default --force
Untuk mencabut dan menghapus public/private key:
php artisan hybrid-encryption:revoke-key-pair default
Command akan meminta konfirmasi. Untuk proses terotomasi:
php artisan hybrid-encryption:revoke-key-pair default --force
Revoke bersifat destruktif terhadap kemampuan decrypt: payload lama yang hanya memiliki pasangan key tersebut tidak dapat dibuka setelah private key dihapus.
Key tersebut disimpan berdasarkan konfigurasi disk, dengan pola:
disk (public): hybrid-encryption/keys/public/users/10/profile/public.pem
disk (private): hybrid-encryption/keys/private/users/10/profile/private.pem
Gunakan service baru berikut agar setiap request memilih key tanpa mengubah konfigurasi global:
use Aptika\HybridEncryption\Services\HybridEncryptionService;
$crypto = app(HybridEncryptionService::class);
$keyId = "users/{$user->id}/profile";
$payload = $crypto->encrypt($keyId, [
'user_id' => $user->id,
'email' => $user->email,
]);
$data = $crypto->decrypt($keyId, $payload);
Memanggil service dari controller
Selain melalui Artisan, pasangan RSA dapat dibuat langsung dari controller
menggunakan KeyPairService. Contoh berikut membuat key pair dengan sumber AES
app_key atau generated berdasarkan request:
use Aptika\HybridEncryption\Services\AesKeyService;
use Aptika\HybridEncryption\Services\HybridEncryptionService;
use Aptika\HybridEncryption\Services\KeyPairService;
use Illuminate\Http\Request;
public function generate(
Request $request,
KeyPairService $keys,
AesKeyService $aes,
HybridEncryptionService $crypto,
) {
$validated = $request->validate([
'key_id' => ['required', 'string', 'max:190'],
'aes_source' => ['nullable', 'in:app_key,generated'],
]);
$source = $validated['aes_source'] ?? 'app_key';
$aesKey = $source === 'generated' ? $aes->generate() : null;
$paths = $keys->generate($validated['key_id']);
return response()->json([
'success' => true,
'key_id' => $paths['key_id'],
'disk' => $paths['disk'],
'visibility' => $paths['visibility'],
'public_path' => $paths['public_path'],
'private_path' => $paths['private_path'],
'aes_source' => $source,
'aes_fingerprint' => $crypto->aesKeyFingerprint($aesKey),
// Hanya dikembalikan untuk generated dan harus langsung diamankan.
'aes_key' => $aesKey,
]);
}
Catatan keamanan:
- Endpoint ini harus dibatasi untuk administrator atau proses internal.
- Jangan mengembalikan isi private key pada response.
aes_keygenerated hanya ditampilkan sekali dan harus disimpan pada secret manager.- Jika key sudah ada,
generate()melemparKeyManagementException. Gunakangenerate($keyId, true)hanya untuk rotasi key yang sudah direncanakan.
API key pair yang tersedia:
use Aptika\HybridEncryption\Services\KeyPairService;
$keys = app(KeyPairService::class);
$paths = $keys->generate('users/10/profile');
$publicPem = $keys->publicKey('users/10/profile');
$privatePem = $keys->privateKey('users/10/profile');
key_id boleh menggunakan huruf, angka, titik, garis bawah, dan garis miring. Jangan mengambil key_id mentah dari input pengguna tanpa validasi aplikasi.
Bentuk payload
AHE3.<base64url dari envelope binary>
encrypt() mengembalikan string opaque, bukan array JSON. Format internalnya
menyimpan komponen berikut secara berurutan:
| Komponen internal | Keterangan |
|---|---|
encrypted_key |
Kunci AES yang dienkripsi RSA-OAEP. |
iv |
Initialization vector AES-GCM 12 byte. |
tag |
Authentication tag AES-GCM. |
data |
Ciphertext payload. |
AHE3 adalah penanda format internal. version, alg, dan aes_source tidak
lagi dikirim sebagai metadata JSON. Penghapusan nama metadata tidak menghapus
komponen kriptografis: iv dan tag wajib untuk AES-GCM, sedangkan
encrypted_key wajib untuk membuka AES key dengan RSA. String yang terpotong,
diubah, atau tidak valid akan ditolak.
Penjelasan algoritma dan tingkat keamanan
AES-256-GCM
AES adalah algoritma enkripsi simetris: key yang sama digunakan untuk encrypt dan decrypt. Standar AES mendefinisikan AES-128, AES-192, dan AES-256 dengan block berukuran 128 bit; angka pada nama AES menunjukkan panjang key. Package ini menggunakan AES-256-GCM, sehingga panjang AES key yang dipakai adalah 256 bit. NIST FIPS 197
GCM adalah mode authenticated encryption. Selain menghasilkan ciphertext, GCM menghasilkan authentication tag. Saat decrypt, tag harus cocok; jika ciphertext, IV, atau tag diubah, decrypt gagal. Package menyimpan nilai iv, tag, dan data dalam payload. NIST menjelaskan bahwa authenticated decryption hanya menghasilkan plaintext jika tag valid, jika tidak hasilnya gagal. NIST SP 800-38D
Package membuat IV acak sepanjang 12 byte untuk setiap payload dan menggunakan tag default PHP OpenSSL sepanjang 16 byte. IV bukan rahasia dan harus dikirim bersama payload, tetapi IV tidak boleh digunakan ulang dengan key AES yang sama. Dokumentasi PHP menyatakan tag GCM dapat berukuran 4 sampai 16 byte dan default-nya 16 byte. PHP openssl_encrypt
RSA-OAEP
RSA adalah enkripsi asimetris: public key digunakan untuk encrypt, sedangkan private key pasangannya digunakan untuk decrypt. Package tidak mengenkripsi seluruh data dengan RSA. RSA hanya membungkus AES key, karena RSA memiliki batas panjang pesan dan lebih lambat untuk payload besar.
Package menggunakan padding RSA-OAEP melalui OPENSSL_PKCS1_OAEP_PADDING. RSAES-OAEP adalah skema enkripsi yang didefinisikan dalam PKCS #1 v2.2 dan dirancang untuk keamanan terhadap serangan chosen-ciphertext pada asumsi matematis RSA dan parameter yang benar. RFC 8017, bagian RSAES-OAEP
Private key RSA harus dirahasiakan. Public key boleh dibagikan, tetapi integritas public key harus diverifikasi sebelum dipasang pada aplikasi pengirim. Jika public key diganti oleh penyerang, data baru dapat dienkripsi ke key penyerang.
Perbandingan tingkat keamanan
Tidak ada satu label resmi "tingkat keamanan dunia" yang berlaku untuk semua kondisi. NIST membandingkan algoritma berdasarkan security strength dalam bit. Tabel NIST yang dirujuk package menunjukkan perkiraan berikut untuk keamanan klasik:
| Algoritma/key | Perkiraan security strength |
|---|---|
| AES-128 | 128 bit |
| RSA-2048 | 112 bit |
| RSA-3072 | 128 bit |
| AES-256 | 256 bit |
Package secara default membuat RSA-3072. Berdasarkan perbandingan NIST, RSA-3072 kira-kira setara dengan AES-128 dalam security strength, bukan AES-256. Karena itu keamanan praktis skema hybrid ini dibatasi oleh komponen RSA-3072 untuk pembungkusan key, sekitar kelas 128-bit secara klasik. AES-256 tetap digunakan untuk isi data, tetapi menambah panjang AES tidak otomatis membuat keseluruhan skema setara RSA-3072 + AES-256. NIST SP 800-57 Part 1 Revision 5
Implementasi saat ini memakai OPENSSL_PKCS1_OAEP_PADDING, tetapi source code tidak memilih digest OAEP secara eksplisit. Karena itu dokumentasi ini tidak mengklaim RSA-OAEP-SHA-256. Jika dua runtime membutuhkan parameter OAEP tertentu, parameter tersebut harus ditetapkan eksplisit dan diuji pada kedua aplikasi. RFC 8017
RSA-3072 dan AES-256 merupakan pilihan kuat untuk banyak aplikasi saat ini jika key, APP_KEY, IV, private key, server, dan implementasi dikelola dengan benar. Pernyataan ini adalah penilaian berdasarkan tabel perbandingan NIST, bukan jaminan bahwa aplikasi bebas dari semua serangan.
Sumber AES key pada package ini
Jika $aesKey kosong, package menggunakan material APP_KEY Laravel sebagai AES-256 key. Untuk format APP_KEY=base64:..., prefix base64: dihapus lalu nilainya di-decode; hasilnya harus 32 byte. Jika $aesKey diberikan, package memakai AES key 32 byte tersebut. AES key tidak dikirim sebagai plaintext di payload; RSA membungkus AES key ke field encrypted_key.
- Mode
app_keypada App1 dan App2 harus memakaiAPP_KEYyang sama. - Mode
generatedpada App1 dan App2 harus memakai AES key generated yang sama. - Jangan mencatat AES key generated atau
APP_KEYke log. Fingerprint AES adalah hash untuk verifikasi dan memang tidak sama dengan nilaiAPP_KEY. - Mengganti
APP_KEYmembuat payload modeapp_keylama tidak dapat didekripsi. - Siapa pun yang memperoleh
APP_KEYdan private RSA key dapat mencoba membuka payload yang ditujukan kepada aplikasi tersebut. - Karena
APP_KEYjuga merupakan root key Laravel, kompromiAPP_KEYdapat berdampak pada fungsi Laravel lain yang menggunakannya. Simpan secret ini di secret manager atau environment yang terlindungi.
Catatan implementasi: mode app_key menggunakan langsung hasil decode APP_KEY; ini bukan password KDF seperti Argon2 atau scrypt. APP_KEY harus dibuat oleh Laravel secara acak dan tidak boleh berupa password buatan manusia. Mode generated menggunakan 32 byte acak dan tidak disimpan oleh package.
Batasan keamanan yang perlu ditangani aplikasi
AES-GCM memberikan kerahasiaan dan deteksi perubahan payload, tetapi package ini belum menyediakan seluruh kontrol keamanan aplikasi:
- Tidak ada proteksi replay. Tambahkan
timestamp, expiry, danrequest_idunik yang dicatat sebagai sudah diproses. - Enkripsi dengan public key tidak membuktikan identitas pengirim. Jika perlu autentikasi pengirim, gunakan TLS dengan autentikasi yang benar atau tanda tangan digital terpisah.
- HTTPS tetap wajib untuk melindungi metadata, endpoint, dan proses pertukaran public key.
- Rotasi
APP_KEYdan RSA key harus direncanakan. Simpan versi/key ID dan lakukan migrasi payload sebelum key lama dihapus. - RSA-3072 dan AES-256 adalah penilaian keamanan klasik. Dokumen NIST yang dirujuk tidak berarti skema ini post-quantum.
Riset dan pemetaan sumber primer yang lebih lengkap tersedia di docs/security-references.md.
Referensi fungsi
HybridEncryptionService::encrypt(string $keyId, array|string $data, ?string $aesKey = null): string
Mengenkripsi array atau string menggunakan public key yang disimpan untuk $keyId. Jika $aesKey kosong, material APP_KEY yang sudah di-decode digunakan sebagai AES key; jika diisi, key tersebut digunakan. IV dibuat acak untuk setiap payload, plaintext dienkripsi dengan AES-GCM, lalu AES key dibungkus memakai RSA-OAEP.
Mengembalikan satu string kompak berawalan AHE3.. Komponen kriptografis
dikemas di dalam string tersebut tanpa metadata JSON yang terlihat. Method
melempar EncryptionException bila public key tidak ditemukan/tidak valid,
pembuatan JSON array gagal, atau proses AES/RSA gagal.
HybridEncryptionService::decrypt(string $keyId, array|string $payload, ?string $aesKey = null): array|string
Membongkar string kompak, membuka kunci AES menggunakan private key untuk
$keyId, lalu mendekripsi data dengan AES-GCM. Format array atau string JSON
lama masih dapat dibaca untuk kompatibilitas. Jika payload dibuat dengan AES
generated, $aesKey yang sama wajib diberikan.
Melempar DecryptionException bila field wajib tidak valid, private key gagal dibuka, Base64 tidak valid, RSA gagal, atau authentication tag AES tidak cocok.
KeyPairService::generate(string $keyId, bool $force = false): array
Membuat RSA public/private key pair dan menyimpannya pada disk serta prefix yang dikonfigurasi. Secara default command menolak menimpa key yang sudah ada. force hanya digunakan untuk rotasi key yang terencana.
KeyPairService::publicKey(string $keyId): string dan privateKey(string $keyId): string
Membaca isi public atau private key dari disk yang sesuai. Jika file tidak ditemukan, method melempar KeyManagementException.
KeyPairService::paths(string $keyId): array
Mengembalikan key_id, nama disk, dan path public/private key tanpa membaca isi key.
AesKeyService::generate(): string
Membuat AES-256 key acak 32 byte dalam format base64:. Key tidak disimpan oleh package dan harus diamankan oleh aplikasi.
AesKeyService::normalize(string $aesKey): string
Mendecode AES key berformat base64: atau menerima 32 byte mentah. Method menolak key yang ukurannya tidak tepat untuk AES-256.
AesKeyService::fingerprint(string $aesKey): string
Menghasilkan fingerprint SHA-256 untuk verifikasi tanpa menampilkan isi AES key.
GenerateAesKeyCommand::handle(AesKeyService $aes): int
Menjalankan command hybrid-encryption:generate-aes-key untuk menghasilkan AES key mandiri tanpa membuat RSA key pair.
KeyPairService::exists(string $keyId): bool
Memeriksa apakah public atau private key untuk key_id sudah ada.
KeyPairService::remove(string $keyId): array
Menghapus public dan private key untuk key_id. Gunakan melalui command revoke agar ada konfirmasi pengguna.
GenerateKeyPairCommand::handle(KeyPairService $keys, HybridEncryptionService $crypto): int
Menjalankan pembuatan key pair berbasis key ID melalui command hybrid-encryption:generate-key-pair. Command bertanya apakah memakai APP_KEY atau membuat AES key generated. AES generated ditampilkan satu kali, tidak disimpan package, dan harus diamankan oleh pengguna. Command juga menampilkan disk, path public/private key, dan fingerprint AES.
RevokeKeyPairCommand::handle(KeyPairService $keys): int
Mencabut dan menghapus public/private key berdasarkan key_id setelah konfirmasi. Opsi --force melewati konfirmasi dan hanya boleh digunakan pada proses terkontrol.
HybridEncryptionService::b64(string $value): string
Helper internal private untuk decode Base64 secara ketat. Melempar DecryptionException bila input tidak valid.
HybridEncryptionServiceProvider::register(): void
Memuat konfigurasi default, mendaftarkan HybridEncryptionService sebagai singleton dengan binding hybrid-encryption, dan membuat alias container untuk dependency injection.
HybridEncryptionServiceProvider::boot(): void
Mendaftarkan publish tag hybrid-encryption-config dan command Artisan package saat aplikasi berjalan di console.
Facades\HybridEncryption::getFacadeAccessor(): string
Menghubungkan facade ke binding container hybrid-encryption.
Exception
EncryptionException: kegagalan public key, serialisasi JSON, enkripsi AES, atau enkripsi RSA.DecryptionException: kegagalan payload, private key, Base64, dekripsi RSA, atau validasi AES.KeyManagementException: kegagalan validasikey_id, pembuatan key, atau pembacaan key dari disk.
Semua exception tersebut mewarisi RuntimeException dan dapat ditangkap aplikasi.
Penanganan error
Jangan kirim detail path key atau exception mentah ke client:
use Aptika\HybridEncryption\Exceptions\DecryptionException;
try {
$data = HybridEncryption::decrypt('features/example', $request->all());
} catch (DecryptionException $exception) {
report($exception);
return response()->json([
'success' => false,
'message' => 'Payload tidak dapat diproses.',
], 422);
}
Testing
composer install
composer test
Test package membuat RSA key sementara, melakukan encrypt/decrypt, dan memastikan data kembali sama. Untuk memakai checkout lokal dari project Laravel, tambahkan repository path:
"repositories": [
{"type": "path", "url": "../aptika-encryption", "options": {"symlink": true}}
]
Kemudian:
composer require aptika/laravel-hybrid-encryption:@dev
php artisan hybrid-encryption:generate-key-pair default
Catatan keamanan dan batasan
- Gunakan HTTPS/TLS saat mengirim payload.
- Pada mode
app_key,APP_KEYApp1 dan App2 harus sama persis agar AES key hasil derivasi sama. Pada modegenerated, kedua aplikasi harus menerima AES key generated yang sama. - Jangan mengganti
APP_KEYjika payload lama masih diperlukan. Jika diganti, payload lama tidak dapat didekripsi. - Jangan commit
private.pem; tambahkan lokasi key ke.gitignore. --forcepada command key pair mengganti key dan membuat ciphertext dengan key lama tidak dapat didekripsi oleh key baru.revoke-key-pairmenghapus public/private key dan membuat payload terkait tidak dapat didekripsi; pastikan backup/migrasi sudah selesai sebelum revoke.- Package menyediakan confidentiality dan integrity melalui AES-GCM, tetapi belum menyediakan proteksi replay. Untuk produksi lintas sistem pertimbangkan
timestamp,request_id,kid, expiry, dan penyimpanan request ID yang sudah diproses. - Pastikan permission directory dan private key dibatasi oleh user aplikasi.
- Jangan menyimpan private key pada disk yang dapat diakses publik atau pada response API.
- Jika private key disimpan di database, enkripsi isi key terlebih dahulu menggunakan mekanisme penyimpanan rahasia aplikasi.
Lisensi
MIT.