From 28e75a28189ab2a5683c096dbf18dd2dae193ae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Sun, 14 Jul 2024 01:46:11 +0200 Subject: [PATCH 01/14] added ARI support --- src/ACMECert.php | 44 +++++++++++++++++++++++++++++++++++++++++++- src/ACMEv2.php | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index 96a20b0..0a49408 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -75,6 +75,45 @@ private function _register($termsOfServiceAgreed=false,$contacts=array(),$extra= return $ret['body']; } + private function getARICertID($pem){ + if (version_compare(PHP_VERSION,'7.1.2','<')){ + throw new Exception('PHP Version >= 7.1.2 required for ARI'); // serialNumberHex - https://github.com/php/php-src/pull/1755 + } + $ret=$this->parseCertificate($pem); + if (!isset($ret['extensions']['authorityKeyIdentifier'])) { + throw new Exception('authorityKeyIdentifier missing'); + } + $aki=hex2bin(str_replace(':','',substr(trim($ret['extensions']['authorityKeyIdentifier']),6))); + if ($aki===false) throw new Exception('Failed to parse authorityKeyIdentifier'); + $ser=hex2bin(trim($ret['serialNumberHex'])); + if ($ser===false) throw new Exception('Failed to parse serial'); + return $this->base64url($aki).'.'.$this->base64url($ser); + } + + public function getARI($pem,&$identifier=null){ + $identifier=$this->getARICertID($pem); + + if (!$this->resources) $this->readDirectory(); + if (!isset($this->resources['renewalInfo'])) throw new Exception('ARI not supported'); + + $ret=$this->http_request($this->resources['renewalInfo'].'/'.$identifier); + + if (!is_array($ret['body']['suggestedWindow'])) throw new Exception('ARI suggestedWindow not present'); + + $sw=&$ret['body']['suggestedWindow']; + + if (!isset($sw['start'])) throw new Exception('ARI suggestedWindow start not present'); + if (!isset($sw['end'])) throw new Exception('ARI suggestedWindow end not present'); + + $sw=array_map(array($this,'parseDate'),$sw); + return $ret['body']; + } + + private function parseDate($str){ + $p=date_parse($str); + return gmmktime($p['hour'],$p['minute'],$p['second'],$p['month'],$p['day'],$p['year']); + } + public function update($contacts=array()){ $this->log('Updating account'); $ret=$this->request($this->getAccountID(),array( @@ -445,7 +484,7 @@ private function parseSettings($opts){ $diff=array_diff_key( $opts, - array_flip(array('authz_reuse','notAfter','notBefore')) + array_flip(array('authz_reuse','notAfter','notBefore','replaces')) ); if (!empty($diff)){ @@ -474,6 +513,9 @@ function($domain){ ); $this->setRFC3339Date($order,'notAfter',$opts); $this->setRFC3339Date($order,'notBefore',$opts); + + if (isset($opts['replaces']))$order['replaces']=$opts['replaces']; + return $order; } diff --git a/src/ACMEv2.php b/src/ACMEv2.php index f6899d6..c089db6 100644 --- a/src/ACMEv2.php +++ b/src/ACMEv2.php @@ -287,7 +287,7 @@ private function json_decode($str){ return $ret; } - private function http_request($url,$data=null){ + protected function http_request($url,$data=null){ if ($this->ch===null) { if (extension_loaded('curl') && $this->ch=curl_init()) { $this->log('Using cURL'); From d1817ba47c20ae680b70d30b05d61235bd1643f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Sun, 14 Jul 2024 02:36:59 +0200 Subject: [PATCH 02/14] version update --- README.md | 2 +- src/ACMEv2.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e75d5af..a362fb8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # ACMECert PHP client library for [Let's Encrypt](https://letsencrypt.org/) and other [ACME v2 - RFC 8555](https://tools.ietf.org/html/rfc8555) compatible Certificate Authorities. -Version: 3.3.1 +Version: 3.4.0 ## Description diff --git a/src/ACMEv2.php b/src/ACMEv2.php index c089db6..c4f3b0c 100644 --- a/src/ACMEv2.php +++ b/src/ACMEv2.php @@ -309,7 +309,7 @@ protected function http_request($url,$data=null){ } $method=$data===false?'HEAD':($data===null?'GET':'POST'); - $user_agent='ACMECert v3.3.1 (+https://github.com/skoerfgen/ACMECert)'; + $user_agent='ACMECert v3.4.0 (+https://github.com/skoerfgen/ACMECert)'; $header=($data===null||$data===false)?array():array('Content-Type: application/jose+json'); if ($this->ch) { $headers=array(); From 188fa7ddbfe409a5737040486a6d975fb9000a34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Sun, 14 Jul 2024 19:01:43 +0200 Subject: [PATCH 03/14] parseDate: fix strtotime bug: https://bugs.php.net/bug.php?id=64814 --- src/ACMECert.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index 0a49408..c092b19 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -110,8 +110,7 @@ public function getARI($pem,&$identifier=null){ } private function parseDate($str){ - $p=date_parse($str); - return gmmktime($p['hour'],$p['minute'],$p['second'],$p['month'],$p['day'],$p['year']); + return strtotime(preg_replace('/(\.\d\d)\d+/','$1',$str)); } public function update($contacts=array()){ From d44f6a0f47bfab1d3085f2974714b4d1eecfe6a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Sun, 14 Jul 2024 19:47:47 +0200 Subject: [PATCH 04/14] added new function getRemainingPercent --- src/ACMECert.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ACMECert.php b/src/ACMECert.php index c092b19..b8114dd 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -455,6 +455,13 @@ public function getRemainingDays($cert_pem){ return ($ret['validTo_time_t']-time())/86400; } + public function getRemainingPercent($cert_pem){ + $ret=$this->parseCertificate($cert_pem); + $total=$ret['validTo_time_t']-$ret['validFrom_time_t']; + $used=time()-$ret['validFrom_time_t']; + return (1-max(0,min(1,$used/$total)))*100; + } + public function generateALPNCertificate($domain_key_pem,$domain,$token){ $domains=array($domain); $csr=$this->generateCSR($domain_key_pem,$domains); From da70feed8df60671783b2eaf42273af160f27759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Sun, 14 Jul 2024 19:48:48 +0200 Subject: [PATCH 05/14] export identifier only on success --- src/ACMECert.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index b8114dd..f86fddc 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -91,12 +91,13 @@ private function getARICertID($pem){ } public function getARI($pem,&$identifier=null){ - $identifier=$this->getARICertID($pem); + $identifier=null; + $id=$this->getARICertID($pem); if (!$this->resources) $this->readDirectory(); if (!isset($this->resources['renewalInfo'])) throw new Exception('ARI not supported'); - $ret=$this->http_request($this->resources['renewalInfo'].'/'.$identifier); + $ret=$this->http_request($this->resources['renewalInfo'].'/'.$id); if (!is_array($ret['body']['suggestedWindow'])) throw new Exception('ARI suggestedWindow not present'); @@ -106,6 +107,8 @@ public function getARI($pem,&$identifier=null){ if (!isset($sw['end'])) throw new Exception('ARI suggestedWindow end not present'); $sw=array_map(array($this,'parseDate'),$sw); + + $identifier=$id; return $ret['body']; } From 853c28ddd36b9eb3e0198aa9975a3f9f537d6775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Sun, 14 Jul 2024 22:05:36 +0200 Subject: [PATCH 06/14] log replaced certificate identifier --- src/ACMECert.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index f86fddc..71526c8 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -523,7 +523,10 @@ function($domain){ $this->setRFC3339Date($order,'notAfter',$opts); $this->setRFC3339Date($order,'notBefore',$opts); - if (isset($opts['replaces']))$order['replaces']=$opts['replaces']; + if (isset($opts['replaces'])) { // ARI + $order['replaces']=$opts['replaces']; + $this->log('Replacing Certificate: '.$opts['replaces']); + } return $order; } From 5c87e69c177e35e251a519b384241efc142c874e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Tue, 16 Jul 2024 00:40:05 +0200 Subject: [PATCH 07/14] readme updated / renamed parameter to ari_cert_id --- README.md | 88 ++++++++++++++++++++++++++++++++++++++++++++++-- src/ACMECert.php | 6 ++-- 2 files changed, 89 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a362fb8..b2776a4 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,8 @@ It is self contained and contains a set of functions allowing you to: - generate [RSA](#acmecertgeneratersakey) / [EC (Elliptic Curve)](#acmecertgenerateeckey) keys - manage account: [register](#acmecertregister)/[External Account Binding (EAB)](#acmecertregistereab)/[update](#acmecertupdate)/[deactivate](#acmecertdeactivateaccount) and [account key roll-over](#acmecertkeychange) - [get](#acmecertgetcertificatechain)/[revoke](#acmecertrevoke) certificates (to renew a certificate just get a new one) -- [parse certificates](#acmecertparsecertificate) / get the [remaining days](#acmecertgetremainingdays) a certificate is still valid +- [parse certificates](#acmecertparsecertificate) / get the [remaining days](#acmecertgetremainingdays) or [percentage](#acmecertgetremainingpercent) a certificate is still valid +- get/use [ACME Renewal Information](#acmecertgetari) (ARI) - and more.. > see [Function Reference](#function-reference) for a full list @@ -312,6 +313,30 @@ $ret=$ac->deactivateAccount(); print_r($ret); ``` +#### Get/Use ACME Renewal Information +```php +$ret=$ac->getARI('file://'.'fullchain.pem',$ari_cert_id); +if ($ret['suggestedWindow']['start']-time()>0) { + die('Certificate still good, exiting..'); +} + +$settings=array( + 'replaces'=>$ari_cert_id +); +$ac->getCertificateChain(..., ..., ..., $settings); +``` + +#### Get Remaining Percentage +```php +$percent=$ac->getRemainingPercent('file://'.'fullchain.pem'); // certificate or certificate-chain +if ($precent>33.333) { // certificate has still more than 1/3 (33.333%) of its lifetime left + die('Certificate still good, exiting..'); +} +// get new certificate here.. +``` +> This allows you to run your renewal script without the need to time it exactly, just run it often enough. (cronjob) + + #### Get Remaining Days ```php $days=$ac->getRemainingDays('file://'.'fullchain.pem'); // certificate or certificate-chain @@ -320,7 +345,6 @@ if ($days>30) { // renew 30 days before expiry } // get new certificate here.. ``` -> This allows you to run your renewal script without the need to time it exactly, just run it often enough. (cronjob) ## Logging @@ -689,6 +713,13 @@ public string ACMECert::getCertificateChain ( mixed $pem, array $domain_config, >> ```php >> array( 'notAfter' => '1970-01-01T01:22:17+01:00' ) >> ``` +>> +>> **`replaces`** (string) +>> +>> The ARI Certificate ID uniquely identifying a previously-issued certificate which this order is intended to replace. +>> +>> Use: [getARI](#acmecertgetari) to get the ARI Certificate ID for a certificate. + ###### Return Values > Returns a PEM encoded certificate chain. @@ -809,6 +840,26 @@ public array ACMECert::parseCertificate ( mixed $pem ) --- +### ACMECert::getRemainingPercent + +Get the percentage the certificate is still valid. + +```php +public float ACMECert::getRemainingPercent( mixed $pem ) +``` +###### Parameters +> **`pem`** +> +> can be one of the following: +> * a string beginning with `file://` containing the filename to read a PEM encoded certificate or certificate-chain from. +> * a string containing the content of a certificate or certificate-chain, PEM encoded, may start with `-----BEGIN CERTIFICATE-----` +###### Return Values +> A float value containing the percentage the certificate is still valid. +###### Errors/Exceptions +> Throws an `Exception` if the certificate could not be parsed. + +--- + ### ACMECert::getRemainingDays Get the number of days the certificate is still valid. @@ -913,6 +964,39 @@ public void ACMECert::setLogger( bool|callable $value = TRUE ) ###### Errors/Exceptions > Throws an `Exception` if the value provided is not boolean or a callable function. +--- +### ACMECert::getARI + +Get ACME Renewal Information (ARI) for a given certificate. + +```php +public array ACMECert::getARI( mixed $pem, string &$ari_cert_id = null ) +``` +###### Parameters +> **`pem`** +> +> can be one of the following: +> * a string beginning with `file://` containing the filename to read a PEM encoded certificate or certificate-chain from. +> * a string containing the content of a certificate or certificate-chain, PEM encoded, may start with `-----BEGIN CERTIFICATE-----` +> +> **`ari_cert_id`** +> +> If this parameter is present, it will be set to the ARI Certificate ID of the given certificate. +> +> See the documentation of [getCertificateChain](#acmecertgetcertificatechain) where the ARI Certificate ID can be used to replace an existing certificate. +###### Return Values +> Returns an Array with the following keys: +> +>> `suggestedWindow` (array) +>> +>> An Array with two keys, `start` and `end`, whose values are unix timestamps, which bound the window of time in which the CA recommends renewing the certificate. +>> +>> `explanationURL` (string, optional) +>> +>> A URL pointing to a page which may explain why the suggested renewal window is what it is. For example, it may be a page explaining the CA's dynamic load-balancing strategy, or a page documenting which certificates are affected by a mass revocation event. +###### Errors/Exceptions +> Throws an `ACME_Exception` if the server responded with an error message or an `Exception` if an other error occured getting the ACME Renewal Information. + --- > MIT License diff --git a/src/ACMECert.php b/src/ACMECert.php index 71526c8..88aece7 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -90,8 +90,8 @@ private function getARICertID($pem){ return $this->base64url($aki).'.'.$this->base64url($ser); } - public function getARI($pem,&$identifier=null){ - $identifier=null; + public function getARI($pem,&$ari_cert_id=null){ + $ari_cert_id=null; $id=$this->getARICertID($pem); if (!$this->resources) $this->readDirectory(); @@ -108,7 +108,7 @@ public function getARI($pem,&$identifier=null){ $sw=array_map(array($this,'parseDate'),$sw); - $identifier=$id; + $ari_cert_id=$id; return $ret['body']; } From 59f59ab70dd3c3496b87f44b678dbafb2c4e75e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Tue, 16 Jul 2024 00:47:04 +0200 Subject: [PATCH 08/14] link to example --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index b2776a4..776345c 100644 --- a/README.md +++ b/README.md @@ -719,6 +719,8 @@ public string ACMECert::getCertificateChain ( mixed $pem, array $domain_config, >> The ARI Certificate ID uniquely identifying a previously-issued certificate which this order is intended to replace. >> >> Use: [getARI](#acmecertgetari) to get the ARI Certificate ID for a certificate. +>> +>> Example: [Get/Use ACME Renewal Information](#getuse-acme-renewal-information) ###### Return Values @@ -984,6 +986,8 @@ public array ACMECert::getARI( mixed $pem, string &$ari_cert_id = null ) > If this parameter is present, it will be set to the ARI Certificate ID of the given certificate. > > See the documentation of [getCertificateChain](#acmecertgetcertificatechain) where the ARI Certificate ID can be used to replace an existing certificate. +> +> Example: [Get/Use ACME Renewal Information](#getuse-acme-renewal-information) ###### Return Values > Returns an Array with the following keys: > From 333f3016c0bc5531645652e3955e7cd51270156e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Tue, 16 Jul 2024 00:53:06 +0200 Subject: [PATCH 09/14] functions moved --- src/ACMECert.php | 82 ++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 41 deletions(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index 88aece7..aae4408 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -75,47 +75,6 @@ private function _register($termsOfServiceAgreed=false,$contacts=array(),$extra= return $ret['body']; } - private function getARICertID($pem){ - if (version_compare(PHP_VERSION,'7.1.2','<')){ - throw new Exception('PHP Version >= 7.1.2 required for ARI'); // serialNumberHex - https://github.com/php/php-src/pull/1755 - } - $ret=$this->parseCertificate($pem); - if (!isset($ret['extensions']['authorityKeyIdentifier'])) { - throw new Exception('authorityKeyIdentifier missing'); - } - $aki=hex2bin(str_replace(':','',substr(trim($ret['extensions']['authorityKeyIdentifier']),6))); - if ($aki===false) throw new Exception('Failed to parse authorityKeyIdentifier'); - $ser=hex2bin(trim($ret['serialNumberHex'])); - if ($ser===false) throw new Exception('Failed to parse serial'); - return $this->base64url($aki).'.'.$this->base64url($ser); - } - - public function getARI($pem,&$ari_cert_id=null){ - $ari_cert_id=null; - $id=$this->getARICertID($pem); - - if (!$this->resources) $this->readDirectory(); - if (!isset($this->resources['renewalInfo'])) throw new Exception('ARI not supported'); - - $ret=$this->http_request($this->resources['renewalInfo'].'/'.$id); - - if (!is_array($ret['body']['suggestedWindow'])) throw new Exception('ARI suggestedWindow not present'); - - $sw=&$ret['body']['suggestedWindow']; - - if (!isset($sw['start'])) throw new Exception('ARI suggestedWindow start not present'); - if (!isset($sw['end'])) throw new Exception('ARI suggestedWindow end not present'); - - $sw=array_map(array($this,'parseDate'),$sw); - - $ari_cert_id=$id; - return $ret['body']; - } - - private function parseDate($str){ - return strtotime(preg_replace('/(\.\d\d)\d+/','$1',$str)); - } - public function update($contacts=array()){ $this->log('Updating account'); $ret=$this->request($this->getAccountID(),array( @@ -486,6 +445,47 @@ public function generateALPNCertificate($domain_key_pem,$domain,$token){ return $out; } + public function getARI($pem,&$ari_cert_id=null){ + $ari_cert_id=null; + $id=$this->getARICertID($pem); + + if (!$this->resources) $this->readDirectory(); + if (!isset($this->resources['renewalInfo'])) throw new Exception('ARI not supported'); + + $ret=$this->http_request($this->resources['renewalInfo'].'/'.$id); + + if (!is_array($ret['body']['suggestedWindow'])) throw new Exception('ARI suggestedWindow not present'); + + $sw=&$ret['body']['suggestedWindow']; + + if (!isset($sw['start'])) throw new Exception('ARI suggestedWindow start not present'); + if (!isset($sw['end'])) throw new Exception('ARI suggestedWindow end not present'); + + $sw=array_map(array($this,'parseDate'),$sw); + + $ari_cert_id=$id; + return $ret['body']; + } + + private function getARICertID($pem){ + if (version_compare(PHP_VERSION,'7.1.2','<')){ + throw new Exception('PHP Version >= 7.1.2 required for ARI'); // serialNumberHex - https://github.com/php/php-src/pull/1755 + } + $ret=$this->parseCertificate($pem); + if (!isset($ret['extensions']['authorityKeyIdentifier'])) { + throw new Exception('authorityKeyIdentifier missing'); + } + $aki=hex2bin(str_replace(':','',substr(trim($ret['extensions']['authorityKeyIdentifier']),6))); + if ($aki===false) throw new Exception('Failed to parse authorityKeyIdentifier'); + $ser=hex2bin(trim($ret['serialNumberHex'])); + if ($ser===false) throw new Exception('Failed to parse serial'); + return $this->base64url($aki).'.'.$this->base64url($ser); + } + + private function parseDate($str){ + return strtotime(preg_replace('/(\.\d\d)\d+/','$1',$str)); + } + private function parseSettings($opts){ // authz_reuse: backwards compatibility to ACMECert v3.1.2 or older if (!is_array($opts)) $opts=array('authz_reuse'=>(bool)$opts); From 3c51f9f9d39ee2301aea17eb0950861bf4f3562e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Tue, 16 Jul 2024 01:24:52 +0200 Subject: [PATCH 10/14] for ARI PHP 7.1.2 or higher --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 776345c..c6c0de1 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ Instead of returning `FALSE` on error, every function in ACMECert throws an [Exc if it fails or an [ACME_Exception](#acme_exception) if the ACME-Server reponded with an error message. ## Requirements -- [x] PHP 5.3 or higher (for EC keys PHP 7.1 or higher is required) +- [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](https://www.php.net/manual/de/book.openssl.php) - [x] enabled [fopen wrappers](https://www.php.net/manual/en/filesystem.configuration.php#ini.allow-url-fopen) (allow_url_fopen=1) **or** [cURL extension](https://www.php.net/manual/en/book.curl.php) From fc09add2fe597adc2d1ba95eecf666a8b0660022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Tue, 16 Jul 2024 01:25:07 +0200 Subject: [PATCH 11/14] composer.json version update --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2dbc324..3466d7b 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,6 @@ { "name": "skoerfgen/acmecert", - "version": "3.3.1", + "version": "3.4.0", "description": "PHP client library for Let's Encrypt and other ACME v2 - RFC 8555 compatible Certificate Authorities", "license": "MIT", "authors": [ From 9dcb0baa572be9784985e3cc91e3af038a469253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Tue, 16 Jul 2024 02:22:43 +0200 Subject: [PATCH 12/14] ARI Certificate ID -> ARI CertID --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index c6c0de1..57d9bc0 100644 --- a/README.md +++ b/README.md @@ -716,9 +716,9 @@ public string ACMECert::getCertificateChain ( mixed $pem, array $domain_config, >> >> **`replaces`** (string) >> ->> The ARI Certificate ID uniquely identifying a previously-issued certificate which this order is intended to replace. +>> The ARI CertID uniquely identifying a previously-issued certificate which this order is intended to replace. >> ->> Use: [getARI](#acmecertgetari) to get the ARI Certificate ID for a certificate. +>> Use: [getARI](#acmecertgetari) to get the ARI CertID for a certificate. >> >> Example: [Get/Use ACME Renewal Information](#getuse-acme-renewal-information) @@ -983,9 +983,9 @@ public array ACMECert::getARI( mixed $pem, string &$ari_cert_id = null ) > > **`ari_cert_id`** > -> If this parameter is present, it will be set to the ARI Certificate ID of the given certificate. +> If this parameter is present, it will be set to the ARI CertID of the given certificate. > -> See the documentation of [getCertificateChain](#acmecertgetcertificatechain) where the ARI Certificate ID can be used to replace an existing certificate. +> See the documentation of [getCertificateChain](#acmecertgetcertificatechain) where the ARI CertID can be used to replace an existing certificate. > > Example: [Get/Use ACME Renewal Information](#getuse-acme-renewal-information) ###### Return Values From 4584f71e11c3591b4d4805db4c2386829358fb96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Wed, 17 Jul 2024 20:53:57 +0200 Subject: [PATCH 13/14] parseDate: throw Exception on error --- src/ACMECert.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index aae4408..60e9ceb 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -483,7 +483,9 @@ private function getARICertID($pem){ } private function parseDate($str){ - return strtotime(preg_replace('/(\.\d\d)\d+/','$1',$str)); + $ret=strtotime(preg_replace('/(\.\d\d)\d+/','$1',$str)); + if ($ret===false) throw new Exception('Failed to parse date: '.$str); + return $ret; } private function parseSettings($opts){ From 95f23a14da9a7920b3af8a0386b7f6bd9f3a8d62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20K=C3=B6rfgen?= Date: Wed, 17 Jul 2024 21:01:11 +0200 Subject: [PATCH 14/14] getARICertID: throw Exception on error --- src/ACMECert.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ACMECert.php b/src/ACMECert.php index 60e9ceb..91525e6 100644 --- a/src/ACMECert.php +++ b/src/ACMECert.php @@ -472,13 +472,19 @@ private function getARICertID($pem){ throw new Exception('PHP Version >= 7.1.2 required for ARI'); // serialNumberHex - https://github.com/php/php-src/pull/1755 } $ret=$this->parseCertificate($pem); + if (!isset($ret['extensions']['authorityKeyIdentifier'])) { throw new Exception('authorityKeyIdentifier missing'); } $aki=hex2bin(str_replace(':','',substr(trim($ret['extensions']['authorityKeyIdentifier']),6))); - if ($aki===false) throw new Exception('Failed to parse authorityKeyIdentifier'); + if (!$aki) throw new Exception('Failed to parse authorityKeyIdentifier'); + + if (!isset($ret['serialNumberHex'])) { + throw new Exception('serialNumberHex missing'); + } $ser=hex2bin(trim($ret['serialNumberHex'])); - if ($ser===false) throw new Exception('Failed to parse serial'); + if (!$ser) throw new Exception('Failed to parse serialNumberHex'); + return $this->base64url($aki).'.'.$this->base64url($ser); }