yourivw/leclient

该软件包已被弃用且不再维护。未建议替代软件包。

PHP Let's Encrypt 客户端库用于 ACME v2

1.3.0 2021-06-20 10:51 UTC

This package is auto-updated.

Last update: 2024-03-20 16:55:19 UTC


README

Latest Stable Version Total Downloads License

 

PHP Let's Encrypt 客户端库用于 ACME v2。此客户端的目标是提供一个易于使用且集成的解决方案,以使用 PHP 创建 Let's Encrypt 签发的 SSL/TLS 证书。用户必须能够访问 Web 服务器或 DNS 管理来验证域名可访问/由用户拥有。

当前版本

当前版本为 1.2.2

入门指南

以下说明将帮助您开始使用此客户端库。如果您有任何问题或发现问题,请随时创建一个问题,我会尽力查看。

还可以查看 Let's Encrypt 文档 以获取有关 Let's Encrypt 和 ACME 的更多信息。

先决条件

最低要求的 PHP 版本是 5.2.0。版本 7.1.0 是 EC 密钥所必需的。当尝试使用低于 7.1.0 的 PHP 版本生成 EC 密钥时,生成 EC 密钥的函数将抛出异常。

将保留 1.0.0 版本,但不再维护。

此客户端还依赖于 cURL 和 OpenSSL。

安装

使用 composer

composer require yourivw/leclient

建议为脚本设置较长的最大执行时间,以减少执行时间。有几种方法可以实现这一点。一种是在页面顶部添加以下内容

ini_set('max_execution_time', 120); // Maximum execution time in seconds.

用法

此处显示了基本函数及其必要的参数。每个类中都包含了扩展说明。

从版本 1.1.6 开始,还可以使用 PSR-3 日志记录器 (\Psr\Log\LoggerInterface) 初始化 LEClient。


初始化客户端

use LEClient\LEClient;

$client = new LEClient($email);								// Initiating a basic LEClient with an array of string e-mail address(es).
$client = new LEClient($email, LEClient::LE_STAGING);					// Initiating a LECLient and use the LetsEncrypt staging URL.
$client = new LEClient($email, LEClient::LE_PRODUCTION);				// Initiating a LECLient and use the LetsEncrypt production URL.
$client = new LEClient($email, true);							// Initiating a LECLient and use the LetsEncrypt staging URL.
$client = new LEClient($email, true, $logger);						// Initiating a LEClient and use a PSR-3 logger (\Psr\Log\LoggerInterface).
$client = new LEClient($email, true, LEClient::LOG_STATUS);				// Initiating a LEClient and log status messages (LOG_DEBUG for full debugging).
$client = new LEClient($email, true, LEClient::LOG_STATUS, 'keys/');			// Initiating a LEClient and select custom certificate keys directory (string or array)
$client = new LEClient($email, true, LEClient::LOG_STATUS, 'keys/', '__account/');	// Initiating a LEClient and select custom account keys directory (string or array)

如果没有找到账户,客户端将自动创建一个新账户。它将转发在初始化过程中提供的电子邮件地址,如上所示。


使用账户函数

$acct = $client->getAccount();  // Retrieves the LetsEncrypt Account instance created by the client.
$acct->updateAccount($email);   // Updates the account with new contact information. Supply an array of string e-mail address(es).
$acct->changeAccountKeys();     // Generates a new RSA keypair for the account and updates the keys with LetsEncrypt.
$acct->deactivateAccount();     // Deactivates the account with LetsEncrypt.

创建证书订单实例。如果找到本地存储的订单,它将使用此订单。否则,它将创建一个新订单。如果提供的域名与订单不匹配,也将创建一个新订单。构建 Let's Encrypt 订单实例

$order = $client->getOrCreateOrder($basename, $domains);                          	    // Get or create order. The basename is preferably the top domain name. This will be the directory in which the keys are stored. Supply an array of string domain names to create a certificate for.
$order = $client->getOrCreateOrder($basename, $domains, $keyType);              	    // Get or create order. keyType can be set to "ec" to get ECDSA certificate. "rsa-4096" is default value. Accepts ALGO-SIZE format.
$order = $client->getOrCreateOrder($basename, $domains, $keyType, $notBefore);              // Get or create order. Supply a notBefore date as a string similar to 0000-00-00T00:00:00Z (yyyy-mm-dd hh:mm:ss).
$order = $client->getOrCreateOrder($basename, $domains, $keyType, $notBefore, $notAfter);   // Get or create order. Supply a notBefore and notAfter date as a string similar to 0000-00-00T00:00:00Z (yyyy-mm-dd hh:mm:ss).

使用订单函数

use LEClient\LEOrder;

$valid      = $order->allAuthorizationsValid();                             // Check whether all authorizations in this order instance are valid.
$pending    = $order->getPendingAuthorizations($type);                      // Get an array of pending authorizations. Performing authorizations is described further on. Type is LEOrder::CHALLENGE_TYPE_HTTP or LEOrder::CHALLENGE_TYPE_DNS.
$verify     = $order->verifyPendingOrderAuthorization($identifier, $type);  // Verify a pending order. The identifier is a string domain name. Type is LEOrder::CHALLENGE_TYPE_HTTP or LEOrder::CHALLENGE_TYPE_DNS.
$deactivate = $order->deactivateOrderAuthorization($identifier);            // Deactivate an authorization. The identifier is a string domain name.
$finalize   = $order->finalizeOrder();                                      // Finalize the order and generate a Certificate Signing Request automatically.
$finalize   = $order->finalizeOrder($csr);                                  // Finalize the order with a custom Certificate Signing Request string.
$finalized  = $order->isFinalized();                                        // Check whether the order is finalized.
$cert       = $order->getCertificate();                                     // Retrieves the certificate and stores it in the keys directory.
$revoke     = $order->revokeCertificate();                                  // Revoke the certificate without a reason.
$revoke     = $order->revokeCertificate($reason);                           // Revoke the certificate with a reason integer as found in section 5.3.1 of RFC5280.

支持函数

use LEClient\LEFunctions;

LEFunctions::RSAGenerateKeys($directory, $privateKeyFile, $publicKeyFile);	// Generate a RSA keypair in the given directory. Variables privateKeyFile and publicKeyFile are optional and have default values private.pem and public.pem.
LEFunctions::ECGenerateKeys($directory, $privateKeyFile, $publicKeyFile);	// Generate a EC keypair in the given directory (PHP 7.1+ required). Variables privateKeyFile and publicKeyFile are optional and have default values private.pem and public.pem.
LEFunctions::Base64UrlSafeEncode($input);					// Encode the input string as a base64 URL safe string.
LEFunctions::Base64UrlSafeDecode($input);					// Decode a base64 URL safe encoded string.
LEFunctions::log($data, $function);						// Print the data. The function variable is optional and defaults to the calling function's name.
LEFunctions::checkHTTPChallenge($domain, $token, $keyAuthorization);		// Checks whether the HTTP challenge is valid. Performing authorizations is described further on.
LEFunctions::checkDNSChallenge($domain, $DNSDigest);				// Checks whether the DNS challenge is valid. Performing authorizations is described further on.
LEFunctions::createhtaccess($directory);					// Created a simple .htaccess file in the directory supplied, denying all visitors.

文件系统结构

LEClient 将账户密钥、证书密钥、证书和订单数据存储在文件系统中。默认情况下,使用的文件夹结构如下,相对于您的当前工作目录

keys/                   Top-level LEClient folder
  public.pem            Your certificate’s public key
  private.pem           Your certificate’s private key
  order                 A file used to store the order URL
  fullchain.crt         The full-chain certificate
  certificate.crt       The certificate
  __account/            An internal folder for LEClient to store your account keys
    public.pem          Your ACME account’s public key
    private.pem         Your ACME account’s private key
    .htaccess           An automatically-generated .htaccess to prevent accidental exposure

您可以通过在创建 LEClient 时传递值来自定义这些位置,传递给 $certificateKeys$accountKeys 构造参数。

传递字符串将更改顶级 LEClient 文件夹的位置和名称,以及账户密钥文件夹的名称。请注意,在传递字符串时,账户密钥文件夹始终是顶级文件夹的子文件夹,这意味着

$client = new LEClient('email@example.com', LEClient::PRODUCTION, LEClient::LOG_OFF, 'path/to/my/key/folder/', 'my_account_folder');

将产生以下结构

path/to/my/key/folder/
  public.pem
  …
  my_account_folder/
    public.pem
    …

如果您想更精确地控制各种文件存储的确切位置,您可以将数组传递给 $certificateKeys$accountKeys 参数。如果向其中一个传递数组,则必须向两个都传递数组。

$client = new LEClient('email@example.com', LEClient::PRODUCTION, LEClient::LOG_OFF, [
  'public_key' => 'path/to/public/key.pem',          // Required
  'private_key' => 'path/to/private/key.pem',        // Required
  'order' => 'path/to/order.txt',                    // Required
  'certificate' => 'path/to/certificate.crt',        // One or both of certificate and fullchain_certificate
  'fullchain_certificate' => 'path/to/fullchain.crt' // must be provided.
], [
  'public_key' => 'path/to/account/public/key.pem',  // Required
  'private_key' => 'path/to/account/private/key.pem' // Required
]);

授权挑战

Let's Encrypt (ACME) 对您希望包含在证书中的域名进行授权,以验证您确实可以访问特定域名。因此,在创建订单时,会为每个域名添加一个授权。如果某个域名最近(在过去的30天内)已被您的账户验证,例如在另一个订单中,则无需再次验证。此时,可以通过对文件(http-01)或DNS TXT记录(dns-01)的HTTP请求来验证域名。客户端通过调用getPendingAuthorizations()提供所选验证所需的数据。由于创建文件或DNS记录因服务器而异,因此客户端不实现此功能。在用户完成挑战要求后,必须调用verifyPendingOrderAuthorization()进行验证。此客户端将首先通过checkHTTPChallenge()或checkDNSChallenge()自行验证挑战,然后开始由Let's Encrypt进行验证。请注意,通配符域名只能通过DNS挑战进行验证。以下显示了两种挑战的示例。

HTTP挑战

对于此示例,我们假设还有一个待验证的域名。

use LEClient\LEOrder;

$pending = $order->getPendingAuthorizations(LEOrder::CHALLENGE_TYPE_HTTP);

这将返回一个数组

Array
(
    [0] => Array
        (
            [type] => http-01
            [identifier] => test.example.org
            [filename] => A8Q1DAVcd_k_oKAC0D_y4ln2IWrRX51jmXnR9UMMtOb
            [content] => A8Q1DAVcd_k_oKAC0D_y4ln2IWrRX51jmXnR9UMMtOb.C4kIiiwfcynb3i48AQVtZRtNrD51z4JiIrdQsgVqcL8
        )
)

对于成功的验证,将向以下URL发起请求

http://test.example.org/.well-known/acme-challenge/A8Q1DAVcd_k_oKAC0D_y4ln2IWrRX51jmXnR9UMMtOb

此文件的应设置为上述数组的文件内容。用户应在此文件可以验证授权之前创建该文件。

DNS挑战

对于此示例,我们假设有两个待验证的域名。一个是通配符域名。此示例中的第二个域名是为了演示目的而添加的。将子域名添加到证书中,而这些子域名也已被通配符域名覆盖,这并不会带来很大的价值。

$pending = $order->getPendingAuthorizations(LEOrder::CHALLENGE_TYPE_DNS);

这将返回一个数组

Array
(
    [0] => Array
        (
            [type] => dns-01
            [identifier] => example.org
            [DNSDigest] => FV5HgbpjIYe1x9MkPI81Nffo2oA-Jo2S88gCL7-Ky5P
        )     
    [1] => Array
        (
            [type] => dns-01
            [identifier] => test.example.org
            [DNSDigest] => WM5YIsgaZQv1b9DbRZ81EwCf2fi-Af2JlgxTC7-Up5D
        )
)

对于成功的验证,DNS记录应创建如下

名称 TTL 类型
_acme-challenge.example.org 60 TXT FV5HgbpjIYe1x9MkPI81Nffo2oA-Jo2S88gCL7-Ky5P
_acme-challenge.test.example.org 60 TXT WM5YIsgaZQv1b9DbRZ81EwCf2fi-Af2JlgxTC7-Up5D

如果需要,可以将TTL值设置得更高,但为了这个目的,我更倾向于将其保持得尽可能低。为了确保验证成功,建议运行一个使用DNS挑战的两部分脚本,中间有一定的时间间隔,以便让DNS记录更新。用户本人应确保在记录可以验证之前设置此DNS记录。DNS记录名称也取决于您的提供商,因此getPendingAuthorizations()不会提供现成的记录名称。某些提供商只接受如_acme-challenge这样的名称,而不包含顶级域名,对于_acme-challenge.example.org。有些提供商接受(需要?)如上所示的全名。

通配符域名,如*.example.org,将验证为example.org,如上所示。这意味着DNS记录名称应该是_acme-challenge.example.org

完整示例

对于HTTP和DNS授权,项目的主要代码目录中有一个完整示例。HTTP授权示例包含在一个文件中。如上所述,DNS授权示例分为两部分,以便在同时允许DNS记录更新。虽然记录的TTL可能很低,但有时在修改后,您的提供商可能需要一些时间来更新您的DNS记录。

如果您无法获取这些示例或客户端库,请尝试查看上面提到的Let's Encrypt文档。为了使示例代码正常工作,请确保将所有'example.org'信息替换为您自己的信息。使用预设示例数据运行这些示例时,示例将失败。

安全性

安全是关于SSL/TLS证书的重要主题,当然是这样的。因为这个客户端是PHP脚本,所以它很可能在Web服务器上运行。很明显,存储在您的Web服务器上的私钥绝不能从Web上访问。当客户端首次创建密钥目录时,它将在该目录中存储一个 .htaccess 文件,拒绝所有访问者。务必确保您的密钥不能从Web上访问!如果您的私钥被公开,我绝不承担责任。如果发生这种情况,最简单的解决方案是更改您的账户密钥(如上所述)或停用您的账户并创建一个新的账户。接下来,创建一个新的证书。

许可证

本项目采用MIT许可证授权 - 有关详细信息,请参阅LICENSE文件。