afosto / yaac
另一个 ACME 客户端:一个解耦的 Let’s Encrypt 客户端
Requires
- ext-json: *
- ext-openssl: *
- guzzlehttp/guzzle: ^6.3|^7.0
- league/flysystem: ^1.0|^3.0
README
用 PHP 编写,此客户端旨在成为一个简化和解耦的 Let’s Encrypt 客户端,基于 ACME V2。
与文件系统或 web 服务器解耦
例如,不是在 nginx 配置下将证书写入磁盘,此客户端仅返回数据(证书和私钥)。
为什么
为什么我需要这个包?在 Afosto,我们像任何其他 SaaS 一样,在多租户环境中运行我们的软件,因此我们无法使用现有的许多客户端。
几乎所有客户端都绑定到一种类型的 web 服务器或固定(一组)域名。如果需要动态获取和安装证书,此包将非常有用。
要求
- PHP7+
- openssl
- Flysystem(任何适配器均可) - 用于存储 Let’s Encrypt 账户信息
入门
入门非常简单。首先安装客户端,然后您需要构建一个 flysystem 文件系统,实例化客户端,然后您可以开始请求证书。
安装
使用 composer 轻松安装此包。
composer require afosto/yaac
实例化客户端
要启动客户端,您需要三样东西;Let’s Encrypt 账户的凭据,一个初始化的 Flysystem,并且您需要决定您想签发 Fake LE Intermediate X1
(测试:MODE_STAGING
)还是 Let's Encrypt Authority X3
(生产:MODE_LIVE
,用于生产)证书。
use League\Flysystem\Filesystem; use League\Flysystem\Adapter\Local; use Afosto\Acme\Client; //Prepare flysystem $adapter = new Local('data'); $filesystem = new Filesystem($adapter); //Construct the client $client = new Client([ 'username' => 'example@example.org', 'fs' => $filesystem, 'mode' => Client::MODE_STAGING, ]);
创建订单
要开始检索证书,我们首先需要创建一个订单。操作如下
在上面的示例中,主要域名后面跟着一个或多个辅助域名。确保您可以为每个域名证明所有权。因此,证书将针对提供的所有域名有效。
$order = $client->createOrder(['example.org', 'www.example.org']);
在创建订单请求中,为提供的每个域名请求一个授权以证明所有权。
证明所有权
在您可以为特定域名获取证书之前,您需要证明您拥有该域名(域)。我们请求授权以证明所有权。为订单获取授权。对于在创建订单请求中提供的每个域名返回一个授权。
$authorizations = $client->authorize($order);
您现在有一个 Authorization
对象的数组。这些对象包含您可以使用的挑战(既可以是 DNS
也可以是 HTTP
),以提供所有权证明。
HTTP 验证
HTTP 验证(在特定 URL 上提供特定内容,例如:example.org/.well-known/acme-challenge/*
)如下进行
使用以下示例开始 HTTP 验证。首先获取挑战,下一步是使挑战可通过以下方式访问
foreach ($authorizations as $authorization) { $file = $authorization->getFile(); file_put_contents($file->getFilename(), $file->getContents()); }
如果您需要通配符证书,则需要使用 DNS 验证,请参阅以下内容
DNS 验证
您还可以使用 DNS 验证 - 为此,您将需要访问 DNS 提供商的 API 以为目标域名创建 TXT 记录。
foreach ($authorizations as $authorization) { $txtRecord = $authorization->getTxtRecord(); //To get the name of the TXT record call: $txtRecord->getName(); //To get the value of the TXT record call: $txtRecord->getValue(); }
自我测试
在通过 HTTP 或 DNS 公开挑战(使其可访问)之后,我们应该进行自我测试,以确保在要求 Let’s Encrypt 验证所有权之前它正常工作。
对于 HTTP 挑战测试调用
if (!$client->selfTest($authorization, Client::VALIDATION_HTTP)) { throw new \Exception('Could not verify ownership via HTTP'); }
对于 DNS 测试调用
if (!$client->selfTest($authorization, Client::VALIDATION_DNS)) { throw new \Exception('Could not verify ownership via DNS'); } sleep(30); // this further sleep is recommended, depending on your DNS provider, see below
使用DNS验证后,当selfTest
确认DNS已更新,建议您在继续之前等待一些额外的时间,例如sleep(30);
。这是因为Let’s Encrypt将执行多视角验证,并且您的DNS提供商可能尚未完成在整个网络中传播更改。
如果您过早地继续,Let’s Encrypt将无法验证。
请求验证
下一步是请求验证所有权。对于每个授权(域名),我们要求Let’s Encrypt验证挑战。
对于HTTP验证
foreach ($authorizations as $authorization) { $client->validate($authorization->getHttpChallenge(), 15); }
对于DNS验证
foreach ($authorizations as $authorization) { $client->validate($authorization->getDnsChallenge(), 15); }
上述代码首先执行自检,如果成功,将尝试15次请求Let’s Encrypt验证挑战(每次间隔1秒)并获取更新状态(Let’s Encrypt验证挑战可能需要几秒钟)。
获取证书
现在要测试是否可以请求订单的证书,按照以下方法检查订单是否已准备好
if ($client->isReady($order)) { //The validation was successful. }
现在我们知道验证已完成,可以获取证书。操作如下
$certificate = $client->getCertificate($order);
现在我们有了证书,将其存储在文件系统上
//Store the certificate and private key where you need it file_put_contents('certificate.cert', $certificate->getCertificate()); file_put_contents('private.key', $certificate->getPrivateKey());
获取单独的中级证书和域名证书
$domainCertificate = $certificate->getCertificate(false); $intermediateCertificate = $certificate->getIntermediate();
谁在使用它?
您是否在使用此包,我们非常乐意了解。请发送PR以列出您的项目或公司。