private function doPost($resource, array $payload) { if (!is_string($resource)) { throw new InvalidArgumentException(sprintf("\$resource must be of type string, %s given.", gettype($resource))); } $privateKey = openssl_pkey_get_private($this->keyPair->getPrivate()); $details = openssl_pkey_get_details($privateKey); if ($details["type"] !== OPENSSL_KEYTYPE_RSA) { throw new \RuntimeException("Only RSA keys are supported right now."); } $uri = (yield $this->getResourceUri($resource)); $attempt = 0; do { $attempt++; if ($attempt > 3) { throw new AcmeException("POST request to {$uri} failed, received too many badNonce errors."); } $enc = new Base64UrlSafeEncoder(); $jws = new SimpleJWS(["alg" => "RS256", "jwk" => ["kty" => "RSA", "n" => $enc->encode($details["rsa"]["n"]), "e" => $enc->encode($details["rsa"]["e"])], "nonce" => (yield $this->getNonce($uri))]); $payload["resource"] = isset($payload["resource"]) ? $payload["resource"] : $resource; $jws->setPayload($payload); $jws->sign($privateKey); $request = (new Request())->setMethod("POST")->setUri($uri)->setBody($jws->getTokenString()); try { /** @var Response $response */ $response = (yield $this->http->request($request)); $this->saveNonce($response); if ($response->getStatus() === 400) { $info = json_decode($response->getBody()); if ($info && isset($info->type) && ($info->type === "urn:acme:badNonce" or $info->type === "urn:acme:error:badNonce")) { continue; } } } catch (Throwable $e) { throw new AcmeException("POST request to {$uri} failed: " . $e->getMessage(), null, $e); } catch (Exception $e) { throw new AcmeException("POST request to {$uri} failed: " . $e->getMessage(), null, $e); } (yield new CoroutineResult($response)); return; } while (true); }