编程

PHP 获取 Let's Encrypt 免费 SSL 证书

107 2024-07-11 02:44:00

这个客户端是用 PHP 编写的,旨在成为一个基于 ACME V2 的简化和解耦的 Let's Encrypt 客户端。

与文件系统或 Web 服务器解耦

例如,这个客户端不需要在 nginx 配置下将证书写入磁盘,而是只返回数据(证书和私钥)。

要求

  • PHP7+
  • openssl
  • Flysystem (任何适配器都可) - 用以存储 Lets Encrypt 账号信息

开始

首先安装客户端,然后需要构建一个 flysystem 文件系统,实例化客户端,然后就可以开始请求证书了。

安装

通过 Composer 安装。

composer require afosto/yaac

初始化客户端

要初始化客户端,需要 3 个东西:一个 Let‘s Encrypt 账号的用户名,一个启动的 Flysystem 文件系统并且你需要确定在哪里签发 Fake LE Intermediate X1(staging: MODE_STAGING) 或者 Let's Encrypt Authority X3 (live: 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,
]);

实例化客户端时,需要时会创建一个新的 Let's Encrypt 帐户,然后同意 TOS。

创建订单

要开始检索证书,我们需要先创建一个订单。具体操作如下:

$order = $client->createOrder(['example.org', 'www.example.org']);

上例中,主域名后紧随着二级域名。请确保每个域名都能够证明所有权。这样,该证书将对所有提供的域名有效。

证明所有权

在获得给定域的证书之前,你需要证明你拥有该域名。我们要求授权以证明所有权。获得订单授权。对于创建订单请求中提供的每个域名,都会返回一个授权。

$authorizations = $client->authorize($order);

现在你有一个 Authorization 对象数组。这些都是你可以使用(DNSHTTP)来提供所有权证明的 challenge。

HTTP 验证

HTTP 验证(其中在域名上的特定 url 处提供特定内容,如:example.org/.aknowledge/acme-challenge/*)如下所示:

使用以下示例进行 HTTP 验证。首先获得 challenges,下一步是使得 challenges 可从中访问

foreach ($authorizations as $authorization) {
    $file = $authorization->getFile();
    file_put_contents($file->getFilename(), $file->getContents());   
}

如果需要通配符证书,需要使用 DNS 验证,如下ow

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();
}

自检

在暴露 challenges (使之可通过 HTTP 或 DNS 访问)后,在要求 Let's Encrypt 验证所有权之前,我们应该进行自检以确保其有效。

对于 HTTP challenge 测试,调用:

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 验证其 challenge。

对于 HTTP 验证:

foreach ($authorizations as $authorization) {
    $client->validate($authorization->getHttpChallenge(), 15);
}

对于 DNS 验证:

foreach ($authorizations as $authorization) {
    $client->validate($authorization->getDnsChallenge(), 15);
}

上述代码首先执行自检,如果成功,将会做 15 次尝试去要求 Lets Encrypt 验证 challenge(有 1 秒钟间隔)并检索更新状态(Lets Encrypt 可能需要一些时间去验证 challenge)。

获取证书

现在要知道我们是否可以申请该订单的证书,请测试订单是否准备好,如下所示:

if ($client->isReady($order)) {
    //The validation was successful.
}

现在我们知道验证完成且可以获取证书。如下代码完成:

$certificate = $client->getCertificate($order);

获取证书后,请将其保存到 the filesystem 文件系统:

//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();

 

PHP