Newer
Older
EnvoyControlPlane / internal / pkg / cert / letsencrypt / renewer.go
package letsencrypt

import (
	internalcertapi "envoy-control-plane/internal/pkg/cert/api"
	"fmt"

	"github.com/go-acme/lego/v4/certificate"
	"github.com/go-acme/lego/v4/registration"
)

// ------------------------------------------------------------------------------------------------

// RenewCertificate renews an existing certificate using go-acme/lego.
func (l *LetsEncryptIssuer) RenewCertificate(oldCert *internalcertapi.Certificate, webrootPath string, email string) (*internalcertapi.Certificate, error) {
	// FIX 1: Load the ACME Account Private Key from the D-value stored in AccountKey.
	acmePrivateKey, err := loadACMEKeyFromDValue(oldCert.AccountKey)
	if err != nil {
		return nil, fmt.Errorf("failed to load ACME account key: %w", err)
	}

	// 2. Setup ACME User (LEOptions) with the loaded account key
	acmeUser := &LEOptions{
		Email: email,
		key:   acmePrivateKey,
		// FIX 2: Provide the Registration URI (KID) to the client for JWS signing.
		// ASSUMPTION: The stored oldCert.AccountURL contains the ACME account URI (KID).
		Registration: &registration.Resource{
			URI: oldCert.AccountURL,
		},
	}

	// 3. Configure and create the ACME client
	client, err := createClient(acmeUser, webrootPath, l.UseStaging)
	if err != nil {
		return nil, err
	}

	// 4. Reconstruct the certificate.Resource for renewal
	certResource := certificate.Resource{
		Domain:      oldCert.Domain,
		Certificate: oldCert.CertPEM,
		// The PrivateKey here is the DOMAIN's old private key.
		PrivateKey: oldCert.KeyPEM,
	}

	// 5. Renew the certificate
	newCertResources, err := client.Certificate.Renew(certResource, false, false, "")
	if err != nil {
		return nil, fmt.Errorf("failed to renew certificate: %w", err)
	}

	// 6. Map the results
	return &internalcertapi.Certificate{
		Domain:  oldCert.Domain,
		CertPEM: newCertResources.Certificate,
		// The renewed key (newCertResources.PrivateKey) is the domain's NEW private key.
		KeyPEM:    newCertResources.PrivateKey,
		FullChain: newCertResources.Certificate,
		// The ACME account key and URL remain the same.
		AccountKey: oldCert.AccountKey,
		AccountURL: oldCert.AccountURL,
	}, nil
}