/** * {@inheritdoc} */ public function generate(KeyPair $keyPair, array $domains) { if (!($privateKey = openssl_pkey_get_private($keyPair->getPrivate()))) { // TODO: Improve error message throw new AcmeException("Couldn't use private key."); } $san = implode(",", array_map(function ($dns) { return "DNS:{$dns}"; }, $domains)); // http://www.heise.de/netze/rfc/rfcs/rfc7633.shtml // http://www.heise.de/netze/rfc/rfcs/rfc6066.shtml $mustStaple = $this->mustStaple ? "tlsfeature = status_request" : ""; $tempFile = tempnam(sys_get_temp_dir(), "acme-openssl-config-"); $tempConf = <<<EOL [ req ] distinguished_name = req_distinguished_name req_extensions = v3_req {$mustStaple} [ req_distinguished_name ] [ v3_req ] basicConstraints = CA:FALSE keyUsage = digitalSignature, nonRepudiation subjectAltName = {$san} EOL; (yield \Amp\File\put($tempFile, $tempConf)); $csr = openssl_csr_new(["CN" => reset($domains)], $privateKey, ["digest_alg" => "sha256", "config" => $tempFile]); (yield \Amp\File\unlink($tempFile)); if (!$csr) { // TODO: Improve error message throw new AcmeException("CSR could not be generated."); } (yield new CoroutineResult(openssl_csr_export($csr, $csr))); }
private function doDelete($name) { Assert::string($name, "Name must be a string. Got: %s"); foreach ((yield \Amp\File\scandir($this->root . "/" . $name)) as $file) { (yield \Amp\File\unlink($this->root . "/" . $name . "/" . $file)); } (yield \Amp\File\rmdir($this->root . "/" . $name)); }
private function doDelete($token) { Assert::string($token, "Token must be a string. Got: %s"); $path = $this->docroot . "/.well-known/acme-challenge/{$token}"; $realpath = realpath($path); if ($realpath) { (yield \Amp\File\unlink($realpath)); } }
private function doRequestCertificate(KeyPair $keyPair, array $domains) { if (empty($domains)) { throw new AcmeException("Parameter \$domains must not be empty."); } if (!($privateKey = openssl_pkey_get_private($keyPair->getPrivate()))) { // TODO: Improve error message throw new AcmeException("Couldn't use private key."); } $tempFile = tempnam(sys_get_temp_dir(), "acme_openssl_config_"); $tempConf = <<<EOL [ req ] distinguished_name = req_distinguished_name req_extensions = v3_req [ req_distinguished_name ] [ v3_req ] basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment subjectAltName = @san [ san ] EOL; $i = 0; $san = implode("\n", array_map(function ($dns) use(&$i) { $i++; return "DNS.{$i} = {$dns}"; }, $domains)); (yield \Amp\File\put($tempFile, $tempConf . "\n" . $san . "\n")); $csr = openssl_csr_new(["CN" => reset($domains), "ST" => "Germany", "C" => "DE", "O" => "Unknown"], $privateKey, ["digest_alg" => "sha256", "req_extensions" => "v3_req", "config" => $tempFile]); (yield \Amp\File\unlink($tempFile)); if (!$csr) { // TODO: Improve error message throw new AcmeException("CSR could not be generated."); } openssl_csr_export($csr, $csr); $begin = "REQUEST-----"; $end = "----END"; $csr = substr($csr, strpos($csr, $begin) + strlen($begin)); $csr = substr($csr, 0, strpos($csr, $end)); $enc = new Base64UrlSafeEncoder(); /** @var Response $response */ $response = (yield $this->acmeClient->post(AcmeResource::NEW_CERTIFICATE, ["csr" => $enc->encode(base64_decode($csr))])); if ($response->getStatus() === 201) { if (!$response->hasHeader("location")) { throw new AcmeException("Protocol Violation: No Location Header"); } (yield new CoroutineResult(current($response->getHeader("location")))); return; } throw $this->generateException($response); }