paragonie/easy-ecc
Easy-ECC is a hardened, easy-to-use PHP wrapper around paragonie/phpecc for elliptic-curve crypto. Generate keypairs, sign/verify messages, and perform ECDH key exchange with Curve25519 or ECDSA curves (K256, P256, P384, P521).
Installation:
composer require paragonie/easy-ecc
Add to composer.json under require:
"paragonie/easy-ecc": "^1.3"
First Use Case: Generate a Curve25519 keypair and sign a message:
use ParagonIE\EasyECC\EasyECC;
$ecc = new EasyECC(); // Defaults to Curve25519
$privateKey = $ecc->generatePrivateKey();
$publicKey = $privateKey->getPublicKey();
$message = "Hello, Easy-ECC!";
$signature = $ecc->sign($message, $privateKey);
// Verify
$isValid = $ecc->verify($message, $publicKey, $signature);
Where to Look First:
Key Generation & Management:
$ecc = new EasyECC('P256'); // NIST P-256
$privateKey = $ecc->generatePrivateKey();
$publicKey = $privateKey->getPublicKey();
// Serialize for storage
$pemPrivate = $privateKey->exportPem();
$compressedPublic = $publicKey->toString();
Signing & Verification:
$signature = $ecc->sign($message, $privateKey, true); // IEEE-P1363 format
$isValid = $ecc->verify($message, $publicKey, $signature, true);
Key Exchange (ECDH):
$bobPrivate = $ecc->generatePrivateKey();
$bobPublic = $bobPrivate->getPublicKey();
$sharedSecretAlice = $ecc->keyExchange($privateKey, $bobPublic, true);
$sharedSecretBob = $ecc->keyExchange($bobPrivate, $publicKey, false);
Asymmetric Encryption (Defuse Integration):
$defuse = new \ParagonIE\EasyECC\Integration\Defuse($ecc);
$encrypted = $defuse->asymmetricEncrypt($data, $privateKey, $bobPublic);
$decrypted = $defuse->asymmetricDecrypt($encrypted, $bobPrivate, $publicKey);
Service Provider Binding:
// app/Providers/AppServiceProvider.php
public function register()
{
$this->app->singleton(EasyECC::class, function () {
return new EasyECC('P256'); // Configure default curve
});
}
Key Storage in Database:
// Model: User.php
protected $casts = [
'private_key_pem' => 'encrypted',
'public_key_compressed' => 'string',
];
public function generateKeys()
{
$ecc = app(EasyECC::class);
$privateKey = $ecc->generatePrivateKey();
$this->private_key_pem = $privateKey->exportPem();
$this->public_key_compressed = $privateKey->getPublicKey()->toString();
$this->save();
}
Middleware for Signed Requests:
// app/Http/Middleware/VerifySignature.php
public function handle($request, Closure $next)
{
$ecc = app(EasyECC::class);
$publicKey = app('public_key_repository')->get($request->user());
$isValid = $ecc->verify($request->getContent(), $publicKey, $request->signature);
if (!$isValid) {
abort(403, 'Invalid signature');
}
return $next($request);
}
Encrypted Attributes:
// Model: EncryptedModel.php
use ParagonIE\EasyECC\Integration\Defuse;
protected $appends = ['encrypted_data'];
protected $defuse;
public function __construct()
{
$this->defuse = new Defuse(app(EasyECC::class));
}
public function getEncryptedDataAttribute()
{
return $this->defuse->unseal($this->attributes['encrypted_data'], $this->privateKey);
}
public function setEncryptedDataAttribute($value)
{
$this->attributes['encrypted_data'] = $this->defuse->seal($value, $this->publicKey);
}
Curve Mismatch:
EasyECC instance.Signature Format Confusion:
sign()/verify() methods accept a boolean for IEEE-P1363 format (default: false for PEM).true for ECDSA signatures requiring IEEE-P1363 (e.g., interoperability with other systems).Key Exchange Direction:
keyExchange() method’s third argument ($isAlice) determines the shared secret derivation.$isAlice between parties will yield different results.true for the initiator and false for the responder.Defuse Integration Quirks:
defuse/php-encryption (composer require defuse/php-encryption).seal()/unseal() for anonymous encryption (no shared key needed).PHP 8.4+ Deprecations:
paragonie/ecc v2.5.0.PHP 8.4 in CI if targeting modern environments.Key Validation:
try {
$publicKey = $ecc->importPublicKey($compressedKey);
} catch (\ParagonIE\EasyECC\EasyECCException $e) {
// Handle invalid key (wrong curve, malformed, etc.)
}
Signature Verification:
var_dump($signature) to check format (PEM vs. IEEE-P1363).Shared Secret Debugging:
$aliceSecret = $ecc->keyExchange($alicePrivate, $bobPublic, true);
$bobSecret = $ecc->keyExchange($bobPrivate, $alicePublic, false);
var_dump(bin2hex($aliceSecret), bin2hex($bobSecret)); // Should match
Custom Encryption Backend:
Implement ParagonIE\EasyECC\EncryptionInterface for non-Defuse symmetric encryption:
class CustomEncryption implements EncryptionInterface {
public function seal($plaintext, PublicKeyInterface $publicKey): string { ... }
public function unseal($ciphertext, PrivateKeyInterface $privateKey): string { ... }
public function asymmetricEncrypt($plaintext, PrivateKeyInterface $privateKey, PublicKeyInterface $publicKey): string { ... }
public function asymmetricDecrypt($ciphertext, PrivateKeyInterface $privateKey, PublicKeyInterface $publicKey): string { ... }
}
Key Storage Abstraction: Create a repository to abstract key storage (e.g., database, Redis):
class KeyRepository {
public function storePrivateKey(PrivateKeyInterface $key, string $userId): void { ... }
public function retrievePublicKey(string $userId): PublicKeyInterface { ... }
}
Event Dispatching: Trigger events for key generation/signing (e.g., Laravel Events):
event(new KeyGenerated($privateKey, $publicKey));
Curve Selection:
Caching:
EasyECC instance in Laravel’s container (singleton).Batch Operations:
How can I help you explore Laravel packages today?