httpClient === null) { $this->httpClient = new HttpClient(); } return $this->httpClient; } public function getCsrFromServer(ServerModel $server) { $response = $this->doSignedRequest($server, '/create-csr', [ 'hostname' => $server->getFqdn() ]); if ($response->getStatusCode() !== 200) { throw new \Exception('Requesting CSR failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents()); } $data = $this->decryptResponse($server, $response); return $data['csr']; } /** * @param ServerModel $server * @throws \Exception */ public function deliverSignedCertificate(ServerModel $server) { $response = $this->doSignedRequest($server, '/deliver-crt', [ 'certificate' => $server->getCertificate() ]); if ($response->getStatusCode() !== 200) { throw new \Exception('Delivering signed certificate failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents()); } $server->setStatus('signed'); $server->save(); } public function deliverOpenVPNConfig(ServerModel $server) { /** @var \Twig_Environment $twig */ $twig = $this->get('twig'); $config = $twig->render('etc/openvpn-server.conf.twig', [ 'server' => $server ]); $response = $this->doSignedRequest($server, '/update-openvpn-config', [ 'config' => $config ]); if ($response->getStatusCode() !== 200) { throw new \Exception('Updating openvpn server config failed (' . $response->getStatusCode() . '): ' . $response->getBody()->getContents()); } } /** * @param ServerModel $server * @param string $path * @param string $data * @return \Psr\Http\Message\ResponseInterface * @throws \Exception */ public function doSignedRequest(ServerModel $server, $path, $data) { $client = $this->getHttpClient(); /** @var CA $ca */ $ca = $this->get('ca'); $data['signature'] = bin2hex($ca->signWithCA($server->getFingerprint())); $json = json_encode($data); $password = bin2hex(openssl_random_pseudo_bytes(48)); $pubKey = openssl_get_publickey($server->getPublicKey()); $success = openssl_public_encrypt($password, $crypted, $pubKey, OPENSSL_PKCS1_PADDING); if (!$success) { throw new \Exception('Encrypting data failed: ' . openssl_error_string() . openssl_error_string()); } $body = bin2hex($crypted) . bin2hex(openssl_encrypt($json, 'blowfish', substr($password, 0, 32), true, substr($password, 32, 8))); return $client->post('http://' . $server->getExternalIp() . ':' . static::MANAGEMENT_PORT . $path, [ 'body' => $body ]); } public function decryptResponse(ServerModel $server, ResponseInterface $response) { $body = $response->getBody()->getContents(); $this->get('logger')->addDebug($body); $encryptedPasswordAndIV = hex2bin(substr($body, 0, 512)); $encryptedBody = hex2bin(substr($body, 512)); /** @var CA $ca */ $ca = $this->get('ca'); $success = openssl_private_decrypt($encryptedPasswordAndIV, $passwordAndIV, $ca->getPrivateKey(), OPENSSL_PKCS1_PADDING); if (!$success) { throw new \Exception('Decrypting data failed: ' . openssl_error_string() . openssl_error_string()); } $json = openssl_decrypt($encryptedBody, 'blowfish', substr($passwordAndIV, 0, 32), true, substr($passwordAndIV, 32, 8)); $data = \json_decode($json, true); $ver = openssl_verify($server->getFingerprint(), hex2bin($data['signature']), $server->getPublicKey(), OPENSSL_ALGO_SHA256); if ($ver === 1) { return $data; } else { throw new \Exception("Checking signature failed ($ver): " . openssl_error_string()); } } }