tomcan / acmeclient
PHP ACME客户端库
Requires
- php: ^8.1
- ext-json: *
- ext-openssl: *
- psr/log: ^2.0|^3.0
- symfony/http-client-contracts: ^2.0|^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.16
- phpstan/phpstan: ^1.10
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);