tomcan/acmeclient

PHP ACME客户端库

安装: 163

依赖: 0

建议者: 0

安全: 0

星标: 1

关注者: 1

分支: 1

开放问题: 0

类型:软件包

v2.2.0 2024-06-04 17:37 UTC

This package is auto-updated.

Last update: 2024-09-04 18:06:35 UTC


README

AcmeClient库是一个PHP库,实现了自动证书管理环境(ACME)协议,用于从像Let's Encrypt这样的服务请求证书。

项目的目标是创建一个灵活的库,允许您在不使用外部工具或客户端的情况下,将ACME证书注册集成到自己的应用程序中(例如具有客户域名的SaaS服务)。

该库既使用接口要求,也使用接口返回,因此您可以将这些接口实现到自己的产品中已存在的类中。如果您不需要那么多集成,您也可以使用内置的类,这些类可以提供完成任务所需的功能。

注意:实际的验证不是本库的作用范围。它会告诉您需要提供什么http或DNS挑战,但确保给定的值最终出现在需要的位置取决于您。

(不)完整性

这并不是旨在完全实现RFC,而是主要关注从ACME兼容服务(例如Let's Encrypt)获取证书的流程。

实现的功能

  • 创建账户
  • 创建订单
  • 授权订单
    • 获取授权
    • 获取挑战
  • 验证订单
  • 最终化订单(获取实际证书)

入门

要开始,您需要将库添加到基于composer的项目中。

composer require tomcan/acmeclient

客户端需要实现Symfony\HttpClientInterface的HTTP客户端(例如Symfony HttpClient),以及ACME服务器的目录URL(默认为Let's Encrypt),以及可选地实现Psr\LoggerInterface接口的记录器类。

$httpClient = \Symfony\Component\HttpClient\HttpClient::create();
$acmeClient = new \TomCan\AcmeClient\AcmeClient($httpClient, 'https://acme-staging-v02.api.letsencrypt.org/directory');

接下来,您需要在ACME服务器上创建一个账户。这需要一个电子邮箱地址和一个用于签名请求的RSA密钥。如果您使用的是内置的Account类,并且未提供密钥,它将自动生成一个新的密钥。然后您需要与目录验证账户。如果是新账户,它将自动创建账户并返回。

// Create new account object
/** @var AccountInterface $account */
$account = new Account('your@email-address.here', null, null);
$acmeClient->getAccount($account);

您应该保存/持久化账户信息以供以后使用。这可以是简单的将信息写入json文件,或通过ORM将其持久化到数据库。但这完全取决于您来实现。

// Example for your convenience only. 

// save Account after having called $acmeClient->getAccount($account) to create the account
$acmeClient->getAccount($account);
file_put_contents('/path/to/account.json', json_encode(['email' => $account->getEmail(), 'url' => $account->getEmail(), 'key' => $account->getKey()]));

// re-loading the account on later requests
$accountData = json_decode(file_get_contents('/path/to/account.json));
$account = new Account($accountData->email, $accountData->url, $accountData->key);
$acmeClient->getAccount($account);

一旦有了账户,您就可以创建一个订单,传递一个包含域名标识符的数组。然后您可能需要授权订单以获取完成所需授权和挑战的列表。在授权订单之前,您可能想要做一些自己的预飞检查,因为每个授权都需要成功才能获得证书。

/** @var OrderInterface */
$order = $acmeClient->createOrder(['yourdomain.com', 'www.yourdomain.com']);
/** @var AuthorizationInterface[] */
$authorizations = $acmeClient->authorize($order);

返回的AuthorizationInterface数组包含每个请求域的授权对象。对于每个授权至少需要验证一个挑战。虽然返回了多个挑战,但您只需要为每个授权验证一个挑战。这可能是一个HTTP挑战或DNS挑战。您需要将授权和选定的挑战作为数组传递,其中授权数组的索引与挑战的索引相对应。

对于HTTP挑战,将向 http://{hostname}/.well-known/acme-challenge/{token} 发送请求。您可以通过ChallengeInterface对象的getToken()方法获取token的值,并通过getValue()方法获取所需的内容。

对于DNS挑战,需要在_acme-challenge.{hostname}上创建一个DNS TXT记录。可以通过ChallengeInterface对象的getValue方法获取DNS记录的值。

// example of using the http-01 HTTP challenge
$challenges = [];
foreach ($authorizations as $authorization) {
    $a[] = $authorization;
    foreach ($authorization->getChallenges() as $challenge) {
        if ($challenge->getType() == 'http-01') {
            $challenges[] = $challenge;
            // e.g. write file to documentroot of webserver
            file_put_contents('/path/to/documentroot/.well-known/acme-challenge/'.$challenge->getToken(), $challenge->getValue());
        }
    }
}
$result = $acmeClient->validate($authorizations, $challenges);

当所有授权完成时,validate方法将返回true;如果失败,则返回false。传入方法中的授权和挑战将被更新以反映状态,因此您可以使用它们来确定哪些实际上是成功的或失败的。

如果所有授权都已完成,您可以完成订单并获取证书。客户端将生成一个新的私钥和csr,并将其传递给ACME服务器。私钥、csr和完整的证书链将通过CertificateInterface对象返回。

/** @var CertificateInterface
$cert = $acmeClient->finalize($order);

参考资料

https://datatracker.ietf.org/doc/html/rfc8555