Implement revoction

This commit is contained in:
Niklas Keller
2015-12-03 10:22:40 +01:00
parent 179e806e0c
commit b1e3839323
3 changed files with 96 additions and 8 deletions

View File

@@ -6,7 +6,7 @@
`kelunik/acme-client` is a standalone ACME client written in PHP.
It's an alternative for the [official client](https://github.com/letsencrypt/letsencrypt) which is written in python.
> **Warning**: This software is under heavy development. Use at your own risk. Revocation is not yet supported by this client.
> **Warning**: This software is under heavy development. Use at your own risk.
## Installation
@@ -18,6 +18,9 @@ composer install
## Usage
> **Note**: This client stores all data in `./data`, be sure to backup this folder regularly.
> It contains your account keys, domain keys and certificates.
Before you can issue certificates, you have to register an account first and read and understand the terms of service of the ACME CA you're using.
For Let's Encrypt there's a [subscriber agreement](https://letsencrypt.org/repository/) you have to accept.
@@ -41,4 +44,12 @@ sudo bin/acme issue \
--path /var/www/example.com
```
For renewal, just run this command again.
For renewal, just run this command again.
To revoke a certificate, you need a valid account key currently, just like for issuance.
```
sudo bin/acme revoke \
--server acme-v01.api.letsencrypt.org/directory \
--cert data/live/example.com/cert.pem
```

View File

@@ -5,9 +5,9 @@
"php": ">=7.0.0",
"ext-posix": "*",
"ext-openssl": "*",
"amphp/aerys": "dev-master",
"bramus/monolog-colored-line-formatter": "^2",
"kelunik/acme": "dev-master",
"kelunik/certificate": "dev-master",
"league/climate": "^3",
"monolog/monolog": "^1.17",
"psr/log": "^1",

View File

@@ -3,20 +3,97 @@
namespace Kelunik\AcmeClient\Commands;
use Amp\Promise;
use Generator;
use Kelunik\Acme\AcmeClient;
use Kelunik\Acme\AcmeException;
use Kelunik\Acme\AcmeService;
use Kelunik\Acme\KeyPair;
use Kelunik\Certificate\Certificate;
use League\CLImate\Argument\Manager;
use Psr\Log\LoggerInterface;
use function Amp\File\exists;
use function Amp\File\get;
use function Amp\resolve;
class Revoke implements Command {
private $logger;
public function __construct(LoggerInterface $logger) {
$this->logger = $logger;
}
public function execute(Manager $args): Promise {
throw new AcmeException("Command not yet implemented!");
return resolve($this->doExecute($args));
}
private function doExecute(Manager $args): Generator {
if (posix_geteuid() !== 0) {
throw new AcmeException("Please run this script as root!");
}
$server = $args->get("server");
$protocol = substr($server, 0, strpos("://", $server));
if (!$protocol || $protocol === $server) {
$server = "https://" . $server;
} elseif ($protocol !== "https") {
throw new \InvalidArgumentException("Invalid server protocol, only HTTPS supported");
}
$keyPair = $this->checkRegistration($args);
$acme = new AcmeService(new AcmeClient($server, $keyPair), $keyPair);
$this->logger->info("Revoking certificate ...");
$pem = yield get($args->get("cert"));
$cert = new Certificate($pem);
if ($cert->getValidTo() < time()) {
$this->logger->warning("Certificate did already expire, no need to revoke it.");
return;
}
$this->logger->info("Certificate was valid for: " . implode(", ", $cert->getNames()));
yield $acme->revokeCertificate($pem);
$this->logger->info("Certificate has been revoked.");
}
private function checkRegistration(Manager $args) {
$server = $args->get("server");
$protocol = substr($server, 0, strpos("://", $server));
if (!$protocol || $protocol === $server) {
$server = "https://" . $server;
} elseif ($protocol !== "https") {
throw new \InvalidArgumentException("Invalid server protocol, only HTTPS supported");
}
$identity = str_replace(["/", "%"], "-", substr($server, 8));
$path = __DIR__ . "/../../data/accounts";
$pathPrivate = "{$path}/{$identity}.private.key";
$pathPublic = "{$path}/{$identity}.public.key";
if (file_exists($pathPrivate) && file_exists($pathPublic)) {
$private = file_get_contents($pathPrivate);
$public = file_get_contents($pathPublic);
$this->logger->info("Found account keys.");
return new KeyPair($private, $public);
}
throw new AcmeException("No registration found for server, please register first");
}
public static function getDefinition(): array {
return [
"domains" => [
"prefix" => "d",
"longPrefix" => "domains",
"description" => "Domains to request a certificate for.",
"cert" => [
"prefix" => "c",
"longPrefix" => "cert",
"description" => "Certificate to be revoked.",
"required" => true,
],
"server" => [