SkillAgentSearch skills...

ACMECert

PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities

Install / Use

/learn @skoerfgen/ACMECert

README

ACMECert v3.7.1

PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities.

Table of Contents

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:

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

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

View on GitHub
GitHub Stars135
CategoryDevelopment
Updated1mo ago
Forks35

Languages

PHP

Security Score

100/100

Audited on Feb 3, 2026

No findings