NovinkaZverejnili sme 50+ Laravel balíkov ako open source
Custom AI apps, agents and automation — Roundly ConsultingRoundly
Všetky balíky
Certificates for Laravel

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ľúčEnvPredvolenéÚčel
directoryCERTIFICATES_ACME_DIRECTORYprodukcia Let’s EncryptURL adresára ACME v2 certifikačnej autority.
contactCERTIFICATES_ACME_CONTACTnullE-mail, ktorý autorita dostane pri registrácii ako kontakt mailto:.
account.key_typeCERTIFICATES_ACME_KEY_TYPEECGenerovaný kľúč účtu: EC (P-256) alebo RSA (2048 bitov) — presne takto, iná hodnota vyhodí výnimku.
account.diskCERTIFICATES_ACME_ACCOUNT_DISKlocalDisk s kľúčom účtu a záznamami účtov.
account.key_pathCERTIFICATES_ACME_ACCOUNT_KEYacme/account.pemCesta ku kľúču účtu na tomto disku.
account.auto_registerCERTIFICATES_ACME_AUTO_REGISTERtrueVygenerovať kľúč pri prvom použití; false vyžaduje kľúč na disku.
solverCERTIFICATES_ACME_SOLVERnullNá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.diskCERTIFICATES_ACME_HTTP_DISKlocalDisk, na ktorý sa zapisujú súbory s tokenmi HTTP-01.
http.pathCERTIFICATES_ACME_HTTP_PATHacme-challengeAdresár pre súbory s tokenmi.
store.diskCERTIFICATES_ACME_STORE_DISKlocalDisk pre vystavený materiál.
store.pathCERTIFICATES_ACME_STORE_PATHcertificatesAdresár pre vystavený materiál.
poll.attemptsCERTIFICATES_ACME_POLL_ATTEMPTS30Maximálny počet kontrol pri overení a finalizácii.
poll.secondsCERTIFICATES_ACME_POLL_SECONDS2Sekundy medzi kontrolami.
verifyCERTIFICATES_ACME_VERIFYtrueOverovanie 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 present

Kaž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 chain
use 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();      // bool

DNS-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 kryptomien

Odoslaní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.