Skip to content
This repository has been archived by the owner on Mar 17, 2024. It is now read-only.

Commit

Permalink
Release version 1.1.1
Browse files Browse the repository at this point in the history
- Fixed 415 Unsupported Media Type error caused by ACME protocol divergence.
- Fixed certificate revocation problem addressed in issue #25.
- Set default acmeURL to the production environment.
- Implemented option to bypass local DNS/HTTP verification verifyPendingOrderAuthorization($identifier, $type, $localcheck = true). It is still making a local verification by default.

Documentation is to be updated.
  • Loading branch information
yourivw committed Mar 29, 2018
1 parent 55facd9 commit ca16431
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 41 deletions.
35 changes: 15 additions & 20 deletions LEClient/LEClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
* @author Youri van Weegberg <[email protected]>
* @copyright 2018 Youri van Weegberg
* @license https://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0
* @version 1.1.1
* @link https://github.com/yourivw/LEClient
* @since Class available since Release 1.0.0
*/
Expand Down Expand Up @@ -67,11 +67,11 @@ class LEClient
* @param boolean $acmeURL ACME URL, can be string or one of predefined values: LE_STAGING or LE_PRODUCTION. Defaults to LE_STAGING.
* @param int $log The level of logging. Defaults to no logging. LOG_OFF, LOG_STATUS, LOG_DEBUG accepted. Defaults to LOG_OFF. (optional)
* @param string $certificateKeys The main directory in which all keys (and certificates), including account keys, are stored. Defaults to 'keys/'. (optional)
* @param array $certificateKeys Optional array containing location of all certificate files. Required paths are public_key, private_key, order and certificate/fullchain_certificate (you can use both or only one of them)
* @param array $certificateKeys Optional array containing location of all certificate files. Required paths are public_key, private_key, order and certificate/fullchain_certificate (you can use both or only one of them)
* @param string $accountKeys The directory in which the account keys are stored. Is a subdir inside $certificateKeys. Defaults to '__account/'.(optional)
* @param array $accountKeys Optional array containing location of account private and public keys. Required paths are private_key, public_key.
* @param array $accountKeys Optional array containing location of account private and public keys. Required paths are private_key, public_key.
*/
public function __construct($email, $acmeURL = LEClient::LE_STAGING, $log = LEClient::LOG_OFF, $certificateKeys = 'keys/', $accountKeys = '__account/')
public function __construct($email, $acmeURL = LEClient::LE_PRODUCTION, $log = LEClient::LOG_OFF, $certificateKeys = 'keys/', $accountKeys = '__account/')
{

$this->log = $log;
Expand All @@ -85,14 +85,13 @@ public function __construct($email, $acmeURL = LEClient::LE_STAGING, $log = LECl
{
$this->baseURL = $acmeURL;
}
else throw new \RuntimeException('acmeURL must be set to string or bool (legacy)');
else throw new \RuntimeException('acmeURL must be set to string or bool (legacy).');

if (is_array($certificateKeys) && is_string($accountKeys)) throw new \RuntimeException('when certificateKeys is array, accountKeys must be array also');
elseif (is_array($accountKeys) && is_string($certificateKeys)) throw new \RuntimeException('when accountKeys is array, certificateKeys must be array also');
if (is_array($certificateKeys) && is_string($accountKeys)) throw new \RuntimeException('When certificateKeys is array, accountKeys must be array too.');
elseif (is_array($accountKeys) && is_string($certificateKeys)) throw new \RuntimeException('When accountKeys is array, certificateKeys must be array too.');

if (is_string($certificateKeys))
{

$certificateKeysDir = $certificateKeys;

if(!file_exists($certificateKeys))
Expand All @@ -108,32 +107,28 @@ public function __construct($email, $acmeURL = LEClient::LE_STAGING, $log = LECl
"fullchain_certificate" => $certificateKeys.'/fullchain.crt',
"order" => $certificateKeys.'/order'
);

}
elseif (is_array($certificateKeys))
{

if (!isset($certificateKeys['certificate']) && !isset($certificateKeys['fullchain_certificate'])) throw new \RuntimeException('certificateKeys[certificate] or certificateKeys[fullchain_certificate] file path must be set');
if (!isset($certificateKeys['private_key'])) throw new \RuntimeException('certificateKeys[private_key] file path must be set');
if (!isset($certificateKeys['certificate']) && !isset($certificateKeys['fullchain_certificate'])) throw new \RuntimeException('certificateKeys[certificate] or certificateKeys[fullchain_certificate] file path must be set.');
if (!isset($certificateKeys['private_key'])) throw new \RuntimeException('certificateKeys[private_key] file path must be set.');
if (!isset($certificateKeys['order'])) $certificateKeys['order'] = dirname($certificateKeys['private_key']).'/order';
if (!isset($certificateKeys['public_key'])) $certificateKeys['public_key'] = dirname($certificateKeys['private_key']).'/public.pem';

foreach ($certificateKeys as $param => $file) {
$parentDir = dirname($file);
if (!is_dir($parentDir)) throw new \RuntimeException($parentDir.' directory not found');
if (!is_dir($parentDir)) throw new \RuntimeException($parentDir.' directory not found.');
}

$this->certificateKeys = $certificateKeys;

}
else
{
throw new \RuntimeException('certificateKeys must be string or array');
throw new \RuntimeException('certificateKeys must be string or array.');
}

if (is_string($accountKeys))
{

$accountKeys = $certificateKeysDir.'/'.$accountKeys;

if(!file_exists($accountKeys))
Expand All @@ -149,12 +144,12 @@ public function __construct($email, $acmeURL = LEClient::LE_STAGING, $log = LECl
}
elseif (is_array($accountKeys))
{
if (!isset($accountKeys['private_key'])) throw new \RuntimeException('accountKeys[private_key] file path must be set');
if (!isset($accountKeys['public_key'])) throw new \RuntimeException('accountKeys[public_key] file path must be set');
if (!isset($accountKeys['private_key'])) throw new \RuntimeException('accountKeys[private_key] file path must be set.');
if (!isset($accountKeys['public_key'])) throw new \RuntimeException('accountKeys[public_key] file path must be set.');

foreach ($accountKeys as $param => $file) {
$parentDir = dirname($file);
if (!is_dir($parentDir)) throw new \RuntimeException($parentDir.' directory not found');
if (!is_dir($parentDir)) throw new \RuntimeException($parentDir.' directory not found.');
}

$this->accountKeys = $accountKeys;
Expand All @@ -167,7 +162,7 @@ public function __construct($email, $acmeURL = LEClient::LE_STAGING, $log = LECl

$this->connector = new LEConnector($this->log, $this->baseURL, $this->accountKeys);
$this->account = new LEAccount($this->connector, $this->log, $email, $this->accountKeys);
if($this->log) LEFunctions::log('LEClient finished constructing', 'function LEClient __construct');
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('LEClient finished constructing', 'function LEClient __construct');
}


Expand Down
4 changes: 2 additions & 2 deletions LEClient/src/LEAccount.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Youri van Weegberg <[email protected]>
* @copyright 2018 Youri van Weegberg
* @license https://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0
* @version 1.1.1
* @link https://github.com/yourivw/LEClient
* @since Class available since Release 1.0.0
*/
Expand Down Expand Up @@ -178,7 +178,7 @@ public function changeAccountKeys()
LEFunctions::RSAgenerateKeys(null, $this->accountKeys['private_key'].'.new', $this->accountKeys['public_key'].'.new');
$privateKey = openssl_pkey_get_private(file_get_contents($this->accountKeys['private_key'].'.new'));
$details = openssl_pkey_get_details($privateKey);
$innerPayload = array('account' => $this->connector->accountURL, 'newKey' => array(
$innerPayload = array('account' => $this->connector->accountURL, 'newKey' => array(
"kty" => "RSA",
"n" => LEFunctions::Base64UrlSafeEncode($details["rsa"]["n"]),
"e" => LEFunctions::Base64UrlSafeEncode($details["rsa"]["e"])
Expand Down
2 changes: 1 addition & 1 deletion LEClient/src/LEAuthorization.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Youri van Weegberg <[email protected]>
* @copyright 2018 Youri van Weegberg
* @license https://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0
* @version 1.1.1
* @link https://github.com/yourivw/LEClient
* @since Class available since Release 1.0.0
*/
Expand Down
4 changes: 2 additions & 2 deletions LEClient/src/LEConnector.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Youri van Weegberg <[email protected]>
* @copyright 2018 Youri van Weegberg
* @license https://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0
* @version 1.1.1
* @link https://github.com/yourivw/LEClient
* @since Class available since Release 1.0.0
*/
Expand Down Expand Up @@ -102,7 +102,7 @@ private function request($method, $URL, $data = null)
{
if($this->accountDeactivated) throw new \RuntimeException('The account was deactivated. No further requests can be made.');

$headers = array('Accept: application/json', 'Content-Type: application/json');
$headers = array('Accept: application/json', 'Content-Type: application/jose+json');
$requestURL = preg_match('~^http~', $URL) ? $URL : $this->baseURL . $URL;
$handle = curl_init();
curl_setopt($handle, CURLOPT_URL, $requestURL);
Expand Down
11 changes: 5 additions & 6 deletions LEClient/src/LEFunctions.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Youri van Weegberg <[email protected]>
* @copyright 2018 Youri van Weegberg
* @license https://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0
* @version 1.1.1
* @link https://github.com/yourivw/LEClient
* @since Class available since Release 1.0.0
*/
Expand All @@ -47,7 +47,7 @@ class LEFunctions
public static function RSAGenerateKeys($directory, $privateKeyFile = 'private.pem', $publicKeyFile = 'public.pem', $keySize = 4096)
{

if ($keySize < 2048 || $keySize > 4096) throw new \RuntimeException("RSA key size must be between 2048 and 4096");
if ($keySize < 2048 || $keySize > 4096) throw new \RuntimeException("RSA key size must be between 2048 and 4096.");

$res = openssl_pkey_new(array(
"private_key_type" => OPENSSL_KEYTYPE_RSA,
Expand Down Expand Up @@ -82,9 +82,8 @@ public static function RSAGenerateKeys($directory, $privateKeyFile = 'private.pe
*/
public static function ECGenerateKeys($directory, $privateKeyFile = 'private.pem', $publicKeyFile = 'public.pem', $keySize = 256)
{
if (version_compare(PHP_VERSION, '7.1.0') == -1) throw new \RuntimeException("PHP 7.1+ required for EC keys");


if (version_compare(PHP_VERSION, '7.1.0') == -1) throw new \RuntimeException("PHP 7.1+ required for EC keys.");

if ($keySize == 256)
{
$res = openssl_pkey_new(array(
Expand All @@ -99,7 +98,7 @@ public static function ECGenerateKeys($directory, $privateKeyFile = 'private.pem
"curve_name" => "secp384r1",
));
}
else throw new \RuntimeException("EC key size must be 256 or 384");
else throw new \RuntimeException("EC key size must be 256 or 384.");


if(!openssl_pkey_export($res, $privateKey)) throw new \RuntimeException("EC keypair export failed!");
Expand Down
19 changes: 10 additions & 9 deletions LEClient/src/LEOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
* @author Youri van Weegberg <[email protected]>
* @copyright 2018 Youri van Weegberg
* @license https://opensource.org/licenses/mit-license.php MIT License
* @version 1.1.0
* @version 1.1.1
* @link https://github.com/yourivw/LEClient
* @since Class available since Release 1.0.0
*/
Expand Down Expand Up @@ -339,10 +339,11 @@ public function getPendingAuthorizations($type)
*
* @param string $identifier The domain name to verify.
* @param int $type The type of verification. Supporting LEOrder::CHALLENGE_TYPE_HTTP and LEOrder::CHALLENGE_TYPE_DNS.
* @param boolean $localcheck Whether to verify the authorization locally before making the authorization request to LE. Optional, default to true.
*
* @return boolean Returns true when the verification request was successful, false if not.
*/
public function verifyPendingOrderAuthorization($identifier, $type)
public function verifyPendingOrderAuthorization($identifier, $type, $localcheck = true)
{
$privateKey = openssl_pkey_get_private(file_get_contents($this->connector->accountKeys['private_key']));
$details = openssl_pkey_get_details($privateKey);
Expand All @@ -368,13 +369,13 @@ public function verifyPendingOrderAuthorization($identifier, $type)
switch($type)
{
case LEOrder::CHALLENGE_TYPE_HTTP:
if(LEFunctions::checkHTTPChallenge($identifier, $challenge['token'], $keyAuthorization))
if($localcheck == false OR LEFunctions::checkHTTPChallenge($identifier, $challenge['token'], $keyAuthorization))
{
$sign = $this->connector->signRequestKid(array('keyAuthorization' => $keyAuthorization), $this->connector->accountURL, $challenge['url']);
$post = $this->connector->post($challenge['url'], $sign);
if(strpos($post['header'], "200 OK") !== false)
{
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('HTTP challenge for \'' . $identifier . '\' valid.', 'function verifyPendingOrderAuthorization');
if($localcheck && $this->log >= LECLient::LOG_STATUS) LEFunctions::log('HTTP challenge for \'' . $identifier . '\' valid.', 'function verifyPendingOrderAuthorization');
while($auth->status == 'pending')
{
sleep(1);
Expand All @@ -385,18 +386,18 @@ public function verifyPendingOrderAuthorization($identifier, $type)
}
else
{
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('HTTP challenge for \'' . $identifier . '\' tested, found invalid.', 'function verifyPendingOrderAuthorization');
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('HTTP challenge for \'' . $identifier . '\' tested locally, found invalid.', 'function verifyPendingOrderAuthorization');
}
break;
case LEOrder::CHALLENGE_TYPE_DNS:
$DNSDigest = LEFunctions::Base64UrlSafeEncode(hash('sha256', $keyAuthorization, true));
if(LEFunctions::checkDNSChallenge($identifier, $DNSDigest))
if($localcheck == false OR LEFunctions::checkDNSChallenge($identifier, $DNSDigest))
{
$sign = $this->connector->signRequestKid(array('keyAuthorization' => $keyAuthorization), $this->connector->accountURL, $challenge['url']);
$post = $this->connector->post($challenge['url'], $sign);
if(strpos($post['header'], "200 OK") !== false)
{
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('DNS challenge for \'' . $identifier . '\' valid.', 'function verifyPendingOrderAuthorization');
if($localcheck && $this->log >= LECLient::LOG_STATUS) LEFunctions::log('DNS challenge for \'' . $identifier . '\' valid.', 'function verifyPendingOrderAuthorization');
while($auth->status == 'pending')
{
sleep(1);
Expand All @@ -407,7 +408,7 @@ public function verifyPendingOrderAuthorization($identifier, $type)
}
else
{
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('DNS challenge for \'' . $identifier . '\' tested, found invalid.', 'function verifyPendingOrderAuthorization');
if($this->log >= LECLient::LOG_STATUS) LEFunctions::log('DNS challenge for \'' . $identifier . '\' tested locally, found invalid.', 'function verifyPendingOrderAuthorization');
}
break;
}
Expand Down Expand Up @@ -629,7 +630,7 @@ public function revokeCertificate($reason = 0)
preg_match('~-----BEGIN\sCERTIFICATE-----(.*)-----END\sCERTIFICATE-----~s', $certificate, $matches);
$certificate = trim(LEFunctions::Base64UrlSafeEncode(base64_decode(trim($matches[1]))));

$sign = $this->connector->signRequestJWK(array('certificate' => $certificate, 'reason' => $reason), $this->connector->revokeCert);
$sign = $this->connector->signRequestJWK(array('certificate' => $certificate, 'reason' => $reason), $this->connector->revokeCert, $this->certificateKeys['private_key']);
$post = $this->connector->post($this->connector->revokeCert, $sign);
if(strpos($post['header'], "200 OK") !== false)
{
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@
"type": "library",
"description": "PHP LetsEncrypt client library for ACME v2",
"require": {
"php": "^7.1.0"
"php": "^5.2"
}
}

0 comments on commit ca16431

Please sign in to comment.