ACME a Let’s Encrypt
Driver acme je natívny klient ACME v2 (RFC 8555). Transport zabezpečuje HTTP klient Laravelu, CSR ext-openssl a každý podpis, JWK aj parsovanie X.509 pochádza z crypto-for-laravel — bez externého ACME či kryptografického SDK. Nasmerujte ho na adresár certifikačnej autority, zadajte kontaktnú adresu a vystavujte:
CERTIFICATES_DRIVER=acme
CERTIFICATES_ACME_DIRECTORY=https://acme-staging-v02.api.letsencrypt.org/directory
CERTIFICATES_ACME_CONTACT=[email protected]
CERTIFICATES_ACME_HTTP_DISK=local
CERTIFICATES_ACME_STORE_DISK=local// config/certificates.php → 'default' => 'acme', or:
Certificates::for('app.example.com')->using('acme')->issue();Počas testovania používajte staging adresár Let’s Encrypt — je uvedený v publikovanej konfigurácii — a na produkčný prepnite, keď vystavovanie funguje.
Kľúče drivera
| Kľúč | Env | Predvolené | Účel |
|---|---|---|---|
directory | CERTIFICATES_ACME_DIRECTORY | produkcia Let’s Encrypt | URL adresára ACME v2 certifikačnej autority. |
contact | CERTIFICATES_ACME_CONTACT | null | E-mail, ktorý autorita dostane pri registrácii ako kontakt mailto:. |
account.key_type | CERTIFICATES_ACME_KEY_TYPE | EC | Generovaný kľúč účtu: EC (P-256) alebo RSA (2048 bitov) — presne takto, iná hodnota vyhodí výnimku. |
account.disk | CERTIFICATES_ACME_ACCOUNT_DISK | local | Disk s kľúčom účtu a záznamami účtov. |
account.key_path | CERTIFICATES_ACME_ACCOUNT_KEY | acme/account.pem | Cesta ku kľúču účtu na tomto disku. |
account.auto_register | CERTIFICATES_ACME_AUTO_REGISTER | true | Vygenerovať kľúč pri prvom použití; false vyžaduje kľúč na disku. |
solver | CERTIFICATES_ACME_SOLVER | null | Názov triedy AcmeChallengeSolver; null alebo prázdna hodnota použije HTTP-01 solver, trieda, ktorá nie je AcmeChallengeSolver, vyhodí výnimku. Typ výzvy určuje metóda type() solvera. |
http.disk | CERTIFICATES_ACME_HTTP_DISK | local | Disk, na ktorý sa zapisujú súbory s tokenmi HTTP-01. |
http.path | CERTIFICATES_ACME_HTTP_PATH | acme-challenge | Adresár pre súbory s tokenmi. |
store.disk | CERTIFICATES_ACME_STORE_DISK | local | Disk pre vystavený materiál. |
store.path | CERTIFICATES_ACME_STORE_PATH | certificates | Adresár pre vystavený materiál. |
poll.attempts | CERTIFICATES_ACME_POLL_ATTEMPTS | 30 | Maximálny počet kontrol pri overení a finalizácii. |
poll.seconds | CERTIFICATES_ACME_POLL_SECONDS | 2 | Sekundy medzi kontrolami. |
verify | CERTIFICATES_ACME_VERIFY | true | Overovanie TLS voči autorite: cesta k CA balíku, true alebo null (1, on, yes) pre systémový balík, alebo false na vypnutie. Pole či float vyhodí výnimku. |
Priebeh objednávky
- Účet sa v nakonfigurovanom adresári zaregistruje, prípadne sa použije existujúci.
- Pre všetky domény sa vytvorí nová objednávka. Autorizácia, ktorú autorita už eviduje ako platnú (nedávno overené autorizácie používa znova), sa preskočí a autorizácia v stave invalid, deactivated, expired či revoked hneď zlyhá. Pri každej čakajúcej autorizácii sa vyberie výzva typu, ktorý vracia type() solvera, solver ju zverejní, autorita dostane pokyn na overenie a autorizácia sa opakovane kontroluje (poll.attempts × poll.seconds). Upratanie solvera prebehne potom vždy, aj pri chybe.
- Vygeneruje sa nový 2048-bitový RSA kľúč certifikátu a CSR pre všetky domény — jeho common name je prvá doména s najviac 64 znakmi (ak sa nijaká nezmestí, žiadne) a v subjectAltName sú všetky domény, takže vystavíte aj hostiteľov dlhých až 253 znakov; objednávka sa finalizuje a kontroluje, kým nie je platná.
- Certifikát sa stiahne, rozdelí na koncový certifikát a reťazec a uloží sa spolu s privátnym kľúčom.
- Každá chyba protokolu vyhodí AcmeException — adresár, nonce, účet, objednávka, výzva, finalizácia alebo stiahnutie. Kontrola, na ktorú autorita odpovie HTTP chybou, zlyhá hneď a s popisom chyby od autority.
HTTP-01
Predvolený solver zapíše key authorization do súboru pomenovaného podľa tokenu v adresári http.path na disku http.disk. Vaša aplikácia musí obsah tohto súboru servírovať na /.well-known/acme-challenge/{token} pre každú overovanú doménu. Po skončení overenia sa súbor zmaže. Token, ktorý nie je base64url (RFC 8555), sa odmietne ešte pred spustením solvera, takže nepriateľská autorita ho nemôže prinútiť zapisovať mimo http.path.
Kľúč účtu
Pri prvom použití sa kľúč účtu vygeneruje a uloží do account.key_path na disku account.disk (account.auto_register): EC vytvorí kľúč P-256, RSA 2048-bitový. Kľúč, ktorý tam umiestnite sami, sa použije bez zmeny — prijímajú sa EC P-256 aj P-384 a každý podpisuje vlastným algoritmom (ES256 / ES384). Ak je auto_register vypnuté a na disku kľúč nie je, vystavenie vyhodí AcmeException.
Samotný účet sa eviduje pre každý adresár autority zvlášť, vedľa kľúča ({key_path}.{sha256(directory)}.kid), a je viazaný na kľúč, s ktorým sa registroval. Prechod zo stagingu na produkciu pri ďalšom vystavení zaregistruje produkčný účet namiesto opakovaného použitia staging účtu, návrat späť použije staging účet a vymenený kľúč sa zaregistruje nanovo, namiesto toho, aby podpisoval pod účtom predchodcu. Opätovná registrácia už známeho kľúča je bezpečná — autorita vráti existujúci účet.
verify riadi overovanie TLS pri spojení s autoritou: cesta k CA balíku, true alebo null pre systémový balík, alebo false na vypnutie (neodporúčame). Akýkoľvek iný reťazec sa číta ako cesta k balíku, takže preklep zlyhá pri handshaku namiesto toho, aby overovanie vypol.
Kam sa certifikát uloží
Vystavený materiál sa zapíše do store.path na disku store.disk, jeden adresár pre každý názov certifikátu:
certificates/ path on the disk
└── generated-tls-app-example-com/ Certificates::certificateName($domain)
├── certificate.pem leaf certificate
├── private.key private key
└── chain.pem intermediate chain, when presentKaždý súbor sa najprv zapíše pod dočasným názvom a na miesto sa presunie, až keď sú zapísané všetky, takže pád ani neúspešný zápis nikdy nenechajú nový certifikát vedľa starého privátneho kľúča; zápis, ktorý disk nahlási ako neúspešný, vyhodí CertificateException. Presuny sú však stále jedno premenovanie na súbor, nie jedna atomická výmena: pád presne medzi dvoma premenovaniami môže nechať nový certificate.pem vedľa starého private.key, kým sa certifikát znova nezapíše. Ak proces počas zápisu spadol, certifikát vystavte znova.
Kontajner viaže kontrakt CertificateStore na to isté úložisko, takže PEM súbory si môžete načítať späť — napríklad pre webový server — a spracovať ich cez CertificateMapper:
use RoundlyConsulting\Certificates\Contracts\CertificateStore;
use RoundlyConsulting\Certificates\Facades\Certificates;
$material = app(CertificateStore::class)->get(Certificates::certificateName('app.example.com'));
$material?->certificatePem; // leaf certificate
$material?->privateKeyPem; // private key
$material?->chainPem; // intermediates, or null
$material?->fullChainPem(); // leaf followed by the chainuse RoundlyConsulting\Certificates\Support\CertificateMapper;
$parsed = app(CertificateMapper::class)->parse($material->certificatePem);
$parsed->commonName; // 'app.example.com', or '' when it has none
$parsed->subjectAltNames; // list<string>
$parsed->notBefore; // CarbonImmutable
$parsed->notAfter; // CarbonImmutable
$parsed->issuer; // issuer CN, or its organization
$parsed->serial; // upper-case hex
$parsed->fingerprint; // upper-case SHA-256 hex
$parsed->selfSigned; // issuer name equals subject name and its own key verifies it
$parsed->isExpired(); // boolDNS-01
DNS-01 je bod rozšírenia — balík nedodáva žiadne SDK DNS poskytovateľa. Rozšírte DnsChallengeSolver a implementujte publishRecord() a removeRecord() nad API vášho DNS poskytovateľa; dostanú názov a hodnotu TXT záznamu:
use RoundlyConsulting\Certificates\ChallengeSolvers\DnsChallengeSolver;
final class Route53Solver extends DnsChallengeSolver
{
protected function publishRecord(string $name, string $value): void { /* upsert TXT */ }
protected function removeRecord(string $name, string $value): void { /* delete TXT */ }
}Potom zaregistrujte názov triedy solvera v drivers.acme.solver (CERTIFICATES_ACME_SOLVER). Nič viac netreba: typ výzvy určuje metóda type() samotného solvera — dns-01 pre DnsChallengeSolver, http-01 pre dodávaný predvolený solver — takže niet samostatného nastavenia typu výzvy, ktoré by ste museli udržiavať v súlade. Solver sa vytvára z kontajnera, takže funguje injektovanie cez konštruktor:
// config/certificates.php — the solver's own type() picks the challenge (dns-01 here)
'acme' => [
// ...
'solver' => App\Acme\Route53Solver::class,
],ACME autority ako Let’s Encrypt pre názov *. nikdy neponúkajú HTTP-01, takže wildcard objednávky potrebujú DNS solver; s predvoleným HTTP-01 solverom wildcard objednávka zlyhá s AcmeException, ktorá to výslovne uvedie. Pre plnú kontrolu implementujte priamo kontrakt AcmeChallengeSolver — solve() aj cleanup() dostanú AcmeChallenge:
namespace RoundlyConsulting\Certificates\Contracts;
interface AcmeChallengeSolver
{
public function type(): string; // 'http-01' or 'dns-01'
public function solve(AcmeChallenge $challenge): void; // publish the challenge
public function cleanup(AcmeChallenge $challenge): void; // remove it afterwards
}$challenge->type; // 'http-01' or 'dns-01'
$challenge->domain; // 'app.example.com'
$challenge->token;
$challenge->keyAuthorization; // token + '.' + account key thumbprint
$challenge->httpPath(); // '/.well-known/acme-challenge/{token}'
$challenge->dnsRecordName(); // '_acme-challenge.app.example.com'
$challenge->dnsRecordValue(); // base64url(SHA-256(key authorization))Prejavte lásku k open source
Tento balík je zadarmo pod licenciou MIT. Ak vám šetrí čas, jednorazový príspevok alebo členstvo na Patreone nám pomôže ho ďalej udržiavať, testovať a dokumentovať.
Ďalšie spôsoby podpory vrátane kryptomienOdoslaním daru súhlasíte s našimi podmienkami prijímania darov.
Chcete to zabudovať do svojho produktu?
Naše balíky integrujeme do zákazkových Laravel a AI riešení. Napíšte nám, na čom pracujete, a ozveme sa do 48 hodín.