ACMECert
PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities
Install / Use
/learn @skoerfgen/ACMECertREADME
ACMECert v3.7.1
PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities.
Table of Contents
- Description
- Requirements
- Require ACMECert
- Usage / Examples
- Logging
- ACME_Exception
- Function Reference
Description
ACMECert is designed to help you set up an automated SSL/TLS certificate/renewal process with just a few lines of PHP.
It is self contained and contains a set of functions allowing you to:
- generate RSA / EC (Elliptic Curve) keys
- manage account: register/External Account Binding (EAB)/update/deactivate and account key roll-over
- get/revoke certificates (to renew a certificate just get a new one)
- parse certificates / get the remaining days or percentage a certificate is still valid
- get/use ACME Renewal Information (ARI)
- get/use ACME certificate profiles
- issue IP address certificates
- and more..
see Function Reference for a full list
It abstracts away the complexity of the ACME protocol to get a certificate (create order, fetch authorizations, compute challenge tokens, polling for status, generate CSR, finalize order, request certificate) into a single function getCertificateChain (or getCertificateChains to also get all alternate chains), where you specify a set of domains you want to get a certificate for and which challenge type to use (all challenge types are supported). This function takes as third argument a user-defined callback function which gets invoked every time a challenge needs to be fulfilled. It is up to you to set/remove the challenge tokens:
$handler=function($opts){
// Write code to setup the challenge token here.
// Return a function that gets called when the challenge token should be removed again:
return function($opts){
// Write code to remove previously setup challenge token.
};
};
$ac->getCertificateChain(..., ..., $handler);
see description of getCertificateChain for details about the callback function.
also see the Get Certificate examples below.
Instead of returning FALSE on error, every function in ACMECert throws an Exception
if it fails or an ACME_Exception if the ACME-Server responded with an error message.
Requirements
- [x] PHP 5.6 or higher (for EC keys PHP 7.1 or higher) (for ARI PHP 7.1.2 or higher)
- [x] OpenSSL extension
- [x] enabled fopen wrappers (allow_url_fopen=1) or cURL extension
Require ACMECert
manual download: https://github.com/skoerfgen/ACMECert/archive/master.zip
usage:
require 'ACMECert.php';
use skoerfgen\ACMECert\ACMECert;
or download it using git:
git clone https://github.com/skoerfgen/ACMECert
usage:
require 'ACMECert/ACMECert.php';
use skoerfgen\ACMECert\ACMECert;
or download it using composer:
composer require skoerfgen/acmecert
usage:
require 'vendor/autoload.php';
use skoerfgen\ACMECert\ACMECert;
Usage / Examples
Choose Certificate Authority (CA)
Let's Encrypt
Live CA
$ac=new ACMECert('https://acme-v02.api.letsencrypt.org/directory');
Staging CA
$ac=new ACMECert('https://acme-staging-v02.api.letsencrypt.org/directory');
Buypass
Live CA
$ac=new ACMECert('https://api.buypass.com/acme/directory');
Staging CA
$ac=new ACMECert('https://api.test4.buypass.no/acme/directory');
Google Trust Services
Live CA
$ac=new ACMECert('https://dv.acme-v02.api.pki.goog/directory');
Staging CA
$ac=new ACMECert('https://dv.acme-v02.test-api.pki.goog/directory');
SSL.com
Live CA
$ac=new ACMECert('https://acme.ssl.com/sslcom-dv-rsa');
ZeroSSL
Live CA
$ac=new ACMECert('https://acme.zerossl.com/v2/DV90');
or any other (ACME v2 - RFC 8555) compatible CA
$ac=new ACMECert('INSERT_URL_TO_ACME_CA_DIRECTORY_HERE');
Generate RSA Private Key
$key=$ac->generateRSAKey(2048);
file_put_contents('account_key.pem',$key);
Equivalent to:
openssl genrsa -out account_key.pem 2048
Generate EC Private Key
$key=$ac->generateECKey('P-384');
file_put_contents('account_key.pem',$key);
Equivalent to:
openssl ecparam -name secp384r1 -genkey -noout -out account_key.pem
Register Account Key with CA
$ac->loadAccountKey('file://'.'account_key.pem');
$ret=$ac->register(true,'info@example.com');
print_r($ret);
Register Account Key with CA using External Account Binding
$ac->loadAccountKey('file://'.'account_key.pem');
$ret=$ac->registerEAB(true,'INSERT_EAB_KEY_ID_HERE','INSERT_EAB_HMAC_HERE','info@example.com');
print_r($ret);
Get Certificate using http-01 challenge
$ac->loadAccountKey('file://'.'account_key.pem');
$domain_config=array(
'test1.example.com'=>array('challenge'=>'http-01','docroot'=>'/var/www/vhosts/test1.example.com'),
'test2.example.com'=>array('challenge'=>'http-01','docroot'=>'/var/www/vhosts/test2.example.com')
);
$handler=function($opts){
$fn=$opts['config']['docroot'].$opts['key'];
@mkdir(dirname($fn),0777,true);
file_put_contents($fn,$opts['value']);
return function($opts){
unlink($opts['config']['docroot'].$opts['key']);
};
};
// Generate new certificate key
$private_key=$ac->generateRSAKey(2048);
$fullchain=$ac->getCertificateChain($private_key,$domain_config,$handler);
file_put_contents('fullchain.pem',$fullchain);
file_put_contents('private_key.pem',$private_key);
Get Certificate using all (http-01,dns-01 and tls-alpn-01) challenge types together
$ac->loadAccountKey('file://'.'account_key.pem');
$domain_config=array(
'example.com'=>array('challenge'=>'http-01','docroot'=>'/var/www/vhosts/example.com'),
'*.example.com'=>array('challenge'=>'dns-01'),
'test.example.org'=>array('challenge'=>'tls-alpn-01')
);
$handler=function($opts) use ($ac){
switch($opts['config']['challenge']){
case 'http-01': // automatic example: challenge directory/file is created..
$fn=$opts['config']['docroot'].$opts['key'];
@mkdir(dirname($fn),0777,true);
file_put_contents($fn,$opts['value']);
return function($opts) use ($fn){ // ..and removed after validation completed
unlink($fn);
};
break;
case 'dns-01': // manual example:
echo 'Create DNS-TXT-Record '.$opts['key'].' with value '.$opts['value']."\n";
readline('Ready?');
return function($opts){
echo 'Remove DNS-TXT-Record '.$opts['key'].' with value '.$opts['value']."\n";
};
break;
case 'tls-alpn-01':
$cert=$ac->generateALPNCertificate('file://'.'some_private_key.pem',$opts['domain'],$opts['value']);
// Use $cert and some_private_key.pem(<- does not have to be a specific key,
// just make sure you generated one) to serve the certificate for $opts['domain']
// This example uses an included ALPN Responder - a standalone https-server
// written in a few lines of node.js - which is able to complete this challenge.
// store the generated verification certificate to be used by the ALPN Responder.
file_put_contents('alpn_cert.pem',$cert);
// To keep this example simple, the included Example ALPN Responder listens on port 443,
// so - for the sake of this example - you have to stop the webserver here, like:
shell_exec('/etc/init.d/apache2 stop');
// Start ALPN Responder (requires node.js)
$resource=proc_open(
'node alpn_responder.js some_private_key.pem alpn_cert.pem',
array(
0=>array('pipe','r'),
1=>array('pipe','w')
),
$pipes
);
// wait until alpn responder is listening
fgets($pipes[1]);
return function($opts) use ($resource,$pipes){
// Stop ALPN Responder
fclose($pipes[0]);
fclose($pipes[1]);
proc_terminate($resource);
proc_close($resource);
shell_exec('/etc/init.d/apache2 start');
};
break;
}
};
// Example for using a pre-generated CSR as input to getCertificateChain instead of a private key:
// $csr=$ac->generateCSR('file://'.'cert_private_key.pem',array_keys($domain_config));
// $fullchain=$ac->getCertificateChain($csr,$domain_config,$handler);
$fullchain=$ac->getCertificateChain('file://'.'cert_private_key.pem',$domain_config,$handler);
file_put_contents('fullchain.pem',$fullchain);
Get alternate chains
$chains=$ac->getCertificateChains('file://'.'cert_private_key.pem',$domain_config,$handler);
if (isset($chains['ISRG Root X1'])){ // use alternate chain 'ISRG Root X1'
$fullchain=$chains['ISRG Root X1'];
}else{ // use default c
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
