spomky-labs/pki-framework
PHP 8.1+ framework for Public Key Infrastructure: X.509 certificates (incl. attribute certs), ASN.1 DER encoding/decoding, X.501/X.520 DN parsing, PEM (RFC 7468) handling, and PKCS-oriented cryptography utilities.
Installation:
composer require spomky-labs/pki-framework
Ensure gmp or bcmath extensions are enabled for cryptographic operations.
First Use Case: Certificate Creation
use SpomkyLabs\PkiFramework\Certificate\Certificate;
use SpomkyLabs\PkiFramework\Certificate\CertificateSubject;
use SpomkyLabs\PkiFramework\Certificate\CertificateIssuer;
use SpomkyLabs\PkiFramework\Certificate\CertificatePublicKey;
use SpomkyLabs\PkiFramework\Certificate\CertificateValidity;
use SpomkyLabs\PkiFramework\Certificate\CertificateExtensions;
// Generate a private key (simplified example)
$privateKey = openssl_pkey_new(['digest_alg' => 'sha256']);
// Create a certificate
$certificate = new Certificate(
new CertificateSubject('CN=example.com'),
new CertificateIssuer('CN=Root CA'),
new CertificatePublicKey(openssl_pkey_get_details($privateKey)['key']),
new CertificateValidity(
new \DateTimeImmutable('-1 year'),
new \DateTimeImmutable('+1 year')
),
new CertificateExtensions()
);
// Sign the certificate (simplified; requires a CA private key)
$certificate->sign($caPrivateKey);
$pem = $certificate->toPem();
First Use Case: Certificate Validation
use SpomkyLabs\PkiFramework\Certificate\CertificateFactory;
$certificate = CertificateFactory::fromPem($pem);
$isValid = $certificate->isValid(new \DateTimeImmutable());
Certificate, CertificateFactory, and CertificateValidator classes.CertificateFactory to create and sign certificates programmatically.
$factory = new CertificateFactory();
$certificate = $factory->createCertificate(
$subject,
$issuer,
$publicKey,
$validity,
$extensions,
$signatureAlgorithm
);
$certificate->sign($caPrivateKey);
app/Services/PkiService.php) to abstract key management and signing logic.$validator = new CertificateValidator();
$validator->addTrustedCertificate($rootCaCertificate);
$isValid = $validator->validate($certificate, new \DateTimeImmutable());
public function handle($request, Closure $next) {
$clientCert = $request->getClientCert();
if (!$this->pkiService->validateCertificate($clientCert)) {
abort(403, 'Invalid client certificate');
}
return $next($request);
}
$crl = new Crl(
$issuer,
new \DateTimeImmutable(),
new \DateTimeImmutable('+1 month'),
[$revokedCertificate1, $revokedCertificate2]
);
$crl->sign($caPrivateKey);
$pem = $crl->toPem();
$derData = file_get_contents('certificate.der');
$asn1 = new Asn1();
$parsed = $asn1->decode($derData);
Key Management:
filesystem or cache to store private keys securely (e.g., encrypted in storage/app/pki/).$privateKey = openssl_pkey_get_private(
file_get_contents(storage_path('app/pki/ca.key'))
);
Configuration:
config/pki.php:
return [
'ca' => [
'path' => storage_path('app/pki/ca.crt'),
'private_key' => storage_path('app/pki/ca.key'),
],
'validation' => [
'trusted_cas' => [storage_path('app/pki/trusted/root.crt')],
],
];
Artisan Commands:
// app/Console/Commands/IssueCertificate.php
public function handle() {
$cert = $this->pkiService->issueCertificate(
$this->argument('subject'),
$this->argument('days')
);
file_put_contents($this->argument('output'), $cert->toPem());
}
app/Console/Kernel.php:
protected $commands = [
Commands\IssueCertificate::class,
];
Event Listeners:
// app/Providers/EventServiceProvider.php
protected $listen = [
\SpomkyLabs\PkiFramework\Events\CertificateIssued::class => [
\App\Listeners\LogCertificateIssuance::class,
],
];
Testing:
pki-framework's test utilities to mock certificates in PHPUnit:
$certificate = CertificateFactory::createSelfSignedCertificate(
'CN=test.example.com',
new \DateTimeImmutable('-1 day'),
new \DateTimeImmutable('+1 day')
);
Key Management:
config/app.php key) or environment variables:
$privateKey = openssl_pkey_get_private(
env('PKI_CA_PRIVATE_KEY')
);
openssl genpkey and update the Laravel config.Time Validation:
DateTimeImmutable with explicit timezone:
$now = new \DateTimeImmutable('now', new \DateTimeZone('UTC'));
$isValid = $certificate->isValid($now);
CRL/OCSP Cache:
$crl = Cache::remember('pki_crl', 6 * 3600, function () {
return CrlFactory::fromPem(file_get_contents($crlPath));
});
ASN.1 Parsing:
Asn1::isValid() or wrap in a try-catch:
try {
$asn1 = new Asn1();
$parsed = $asn1->decode($derData);
} catch (\SpomkyLabs\PkiFramework\Exception\Asn1Exception $e) {
Log::error('Invalid ASN.1 data', ['error' => $e->getMessage()]);
return false;
}
Signature Algorithms:
$certificate->sign($privateKey, 'sha256WithRSAEncryption');
Performance:
viaMiddleware for non-critical paths.How can I help you explore Laravel packages today?