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. Pri každej 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; 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.

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.

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

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'
$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->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.