fsotomsk / leclient
PHP Let's Encrypt 客户端库用于 ACME v2
Requires
- php: >=7.2
- ext-curl: *
- ext-json: *
- ext-openssl: *
README
PHP Let's Encrypt 客户端库用于 ACME v2。此客户端的目的是创建一个易于使用且集成的解决方案,通过 PHP 创建 Let's Encrypt 颁发的 SSL/TLS 证书。用户必须能够访问 Web 服务器或 DNS 管理,以便能够验证域名可访问或属于用户。
当前版本
当前版本是 1.1.6
以下示例代码需要更新。
此客户端是使用 Let's Encrypt 阶段性服务器为版本 2 开发的。虽然版本 2 目前仍在由 Let's Encrypt 开发和实施,因此项目可能会发生变化。
入门指南
以下说明将指导您开始使用此客户端库。如果您有任何问题或发现问题,请随意提交一个问题,我会尽力查看。
还可以查看 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 fsotomsk/leclient
尽管可以将此添加到您自己的自动加载器中,但并不推荐这样做,因为您将无法控制依赖项。如果您以前没有使用过 composer,我强烈建议您在 https://getcomposer.org 上查看。
建议您通过设置更高的最大执行时间来给脚本一些宽松的时间。有几种方法可以做到这一点。一种方法是在页面顶部添加以下内容
ini_set('max_execution_time', 120); // Maximum execution time in seconds.
用法
此处显示基本函数及其必需的参数。每个类中都包含扩展描述。
初始化客户端
use LEClient\LEClient; $client = new LEClient($email); // Initiating a basic LEClient with an array of string e-mail address(es). $client = new LEClient($email, true); // Initiating a LECLient and use the LetsEncrypt staging URL. $client = new LEClient($email, true, LEClient::LOG_STATUS); // Initiating a LEClient and log status messages (LOG_DEBUG for full debugging).
如果找不到新账户,客户端将自动创建一个新账户。它将转发在上面的初始化过程中提供的电子邮件地址。
使用账户函数
$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" is default value. $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).
使用订单函数
$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, under the specific order (basename). $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.
授权挑战
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值设置得更高,但我更喜欢尽可能保持它尽可能低。为了确保验证成功,建议运行一个使用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文档。
安全性
安全性是关于SSL/TLS证书的重要主题。由于此客户端是PHP脚本,它很可能在Web服务器上运行。显然,您的私钥,存储在您的Web服务器上,不应从网络上访问。当客户端第一次创建密钥目录时,它会在该目录中存储一个.htaccess文件,拒绝所有访问者。始终确保您的密钥不能从网络上访问!我不会以任何方式对您的私钥公开负责。如果发生这种情况,最简单的解决方案是更改上述账户密钥或停用账户并创建一个新账户。接下来,创建一个新的证书。
许可证
本项目采用MIT许可协议 - 请参阅LICENSE文件获取详细信息。