ca

package
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 28, 2026 License: Apache-2.0 Imports: 43 Imported by: 0

Documentation

Overview

Package ca is the CA engine: it issues end-entity certificates from a preset, after checking everything a request contains (PHASE1 step 7).

Order of an issuance – every check runs before anything is signed:

  1. preset and CA (active, key online, unsealed, CA certificates valid now)
  2. key: verify the CSR's signature and apply the key policy, or generate the key on the server if the tenant allows it
  3. names: the CSR's names are only a wish list; every name is checked, the preset's SAN rules apply, then the name constraints and EKU limits of every CA up to the root
  4. validity: explicit requests are accepted or denied, defaults adapt
  5. build from the preset (never from the CSR), sign, check the result against the chain, store certificate, names, key and audit event in one pki.db transaction

Refused requests are written to the tenant audit log as denied (policy) or failure (sealed, internal error).

Index

Constants

View Source
const (
	KeyModeOffline         = "offline"
	KeyModeOnlineEncrypted = "online_encrypted"
	KeyModeHSM             = "hsm"
)

Key modes (cas.key_mode, ADR-09).

View Source
const (
	StatusActive           = "active"
	StatusPendingSignature = "pending_signature"
	StatusRetired          = "retired"
)

CA statuses (cas.status).

View Source
const (
	CRLOK       = "ok"
	CRLRenewDue = "renew_due" // a third of the lifetime has passed: the maintainer renews it
	CRLWarning  = "warning"   // half has passed: the renewal didn't happen
	CRLExpired  = "expired"   // past nextUpdate: clients that check revocation fail
	CRLMissing  = "missing"   // never published
)

CRL states, worst last.

View Source
const (
	ActionDomainCreate = "domain.create"
	ActionDomainUpdate = "domain.update"
	ActionDomainDelete = "domain.delete"
)

Audit actions of domains.

View Source
const (
	ApprovalOpen    = "open"     // any ACME account that passes the challenge
	ApprovalAccount = "account"  // the account must be approved (or bound with EAB) – the default
	ApprovalNewName = "new_name" // the first issuance for a name needs approval
)

Approval levels (PLAN §6.3).

View Source
const (
	ChallengeHTTP01 = "http-01"
	ChallengeDNS01  = "dns-01"
	AddressInternal = "internal" // http-01 may reach private networks
	AddressPublic   = "public"   // http-01 may reach public addresses only
)

Challenge types and address policies.

View Source
const (
	ExportPKCS8PEM    = "pkcs8-pem"
	ExportPKCS12      = "pkcs12"
	ExportCombinedPEM = "combined-pem"
)

Export formats.

View Source
const (
	JobSucceeded = "succeeded" // imported
	JobChecked   = "checked"   // a dry run without problems
	JobFailed    = "failed"    // refused (problems) or broken
)

Import job states.

View Source
const (
	ActionCAImportLegacy = "ca.import_legacy"
	ActionImportLegacy   = "import.legacy"
)

Audit actions of the import.

View Source
const (
	AlgECDSAP256 = "ecdsa-p256"
	AlgECDSAP384 = "ecdsa-p384"
	AlgECDSAP521 = "ecdsa-p521"
	AlgEd25519   = "ed25519"
	AlgRSA2048   = "rsa-2048"
	AlgRSA3072   = "rsa-3072"
	AlgRSA4096   = "rsa-4096"

	// DefaultKeyAlgorithm is used for server-side key generation when the
	// request names none: what certbot, Traefik and Caddy generate too.
	DefaultKeyAlgorithm = AlgECDSAP256
)

Key algorithm names, as stored in keys.algorithm and certificates.key_algorithm.

View Source
const (
	ActionCACreate      = "ca.create"
	ActionCAImportRoot  = "ca.import_root"
	ActionCACSRCreate   = "ca.csr_create"
	ActionCACertInstall = "ca.certificate_install"
	ActionCARenew       = "ca.renew"
	ActionCARetire      = "ca.retire"
	ActionCAUpdate      = "ca.update"
)

Audit actions of the CA lifecycle.

View Source
const (
	KindRoot         = "root"
	KindIntermediate = "intermediate"
)

Kinds of CA (cas.kind).

View Source
const (
	ActionPlanRevocation   = "certificate.revocation.plan"
	ActionCancelRevocation = "certificate.revocation.cancel"
)
View Source
const (
	DefaultCAKeyAlgorithm      = AlgECDSAP384
	DefaultRootDays            = 7300 // like legacy ROOT_DAYS
	DefaultIntermediateDays    = 3650 // like legacy INTERMEDIATE_DAYS
	DefaultCRLDaysOfflineRoot  = 180  // re-signed offline
	DefaultCRLDaysOnlineRoot   = 30
	DefaultCRLDaysIntermediate = 7 // renewed by the server
	DefaultMaxValidityDays     = 397
)

Defaults for new CAs (owner decisions 2026-10-14, step 8 e).

View Source
const (
	CertValid   = "valid"
	CertRevoked = "revoked"
	CertExpired = "expired"
)

Certificate statuses (API).

View Source
const (
	DefaultPageSize = 50
	MaxPageSize     = 100
)

Page sizes of ListCertificates.

View Source
const (
	ActionRevoke    = "certificate.revoke"
	ActionCARevoke  = "ca.revoke"
	ActionCRLSign   = "crl.sign"
	ActionCRLUpload = "crl.upload"
)

Audit actions of revocation and CRLs.

View Source
const (
	ReasonUnspecified          = "unspecified"
	ReasonKeyCompromise        = "keyCompromise"
	ReasonCACompromise         = "cACompromise"
	ReasonAffiliationChanged   = "affiliationChanged"
	ReasonSuperseded           = "superseded"
	ReasonCessationOfOperation = "cessationOfOperation"
	ReasonPrivilegeWithdrawn   = "privilegeWithdrawn"
)

Revocation reasons (RFC 5280 §5.3.1) – only final ones: no certificateHold/removeFromCRL (reversible, banned by the CA/B Forum).

View Source
const ActionImportExternal = "certificate.import_external"

ActionImportExternal is the audit action of an external certificate.

View Source
const ActionIssue = "certificate.issue"

ActionIssue is the audit action of an issuance, allowed or not.

View Source
const ActionKeyDestroy = "key.destroy"

ActionKeyDestroy is the audit action of a destroyed key.

View Source
const ActionKeyDownload = "key.download"

ActionKeyDownload is the audit action of a private key download.

View Source
const Backdate = 5 * time.Minute

Backdate moves notBefore into the past, so a client whose clock is a few minutes behind accepts a fresh certificate (pki-rules.md §5).

View Source
const DefaultACMEValidityDays = 90

DefaultACMEValidityDays: ACME issuances default to 90 days, capped by the CA's maximum (owner decision 6, 2026-10-13).

View Source
const ExpiringWindow = 30 * day

ExpiringWindow: a valid certificate that ends within it "expires soon".

View Source
const MaxCRLSize = 4 << 20

MaxCRLSize limits an uploaded CRL.

View Source
const MaxCSRSize = 64 << 10

MaxCSRSize limits an uploaded CSR. Real CSRs are a few KiB; the limit keeps a hostile upload from costing memory and parse time.

View Source
const MaxCertSize = 64 << 10

MaxCertSize limits an uploaded certificate.

Variables

Approvals, Challenges and AddressPolicies list the valid values.

LeafReasons and CAReasons are the reasons a request may use.

CAAlgorithms are the key algorithms for CAs MyOwnPKI creates. RSA 2048 and P-521 are only accepted (with a warning) for imported CAs; Ed25519 chains aren't accepted by browsers and Windows, so never for a CA.

View Source
var ErrDenied = errors.New("request denied")

ErrDenied is wrapped by every refusal the requester or an admin can fix: a policy, a name, a CSR, a limit. Everything else is a failure.

View Source
var ErrKeyDeleted = errors.New("the private key was deleted from the server after an earlier download")

ErrKeyDeleted: the key was destroyed after an earlier download.

View Source
var ErrNoStoredKey = errors.New("the private key of this certificate is not on the server – it was issued from a CSR")

ErrNoStoredKey: the certificate was issued from a CSR – its key never was on this server.

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound: no such object, or the caller may not read it.

ExportFormats lists the formats in the order the UI offers them.

GeneratedAlgorithms are the algorithms the server generates keys for.

View Source
var SystemActor = keys.Who{Actor: audit.Actor{Type: audit.ActorSystem, Name: "crl-maintainer"}, Principal: authz.System("crl-maintainer")}

SystemActor is who acts in background work (CRL renewal).

Functions

func ACMENameSet added in v0.3.1

func ACMENameSet(raw string) (map[string]struct{}, error)

ACMENameSet parses the normalized, duplicate-free identifier set stored on an order. The keys have the same dns:/ip: form as ACME's identifier.key().

func AttachCRLStatus

func AttachCRLStatus(cas []CAInfo, statuses []CRLStatus)

AttachCRLStatus puts each generation's CRL status into the CA list.

func CRLURL

func CRLURL(baseURL, caName string, generation int64) string

CRLURL is the CRL distribution point of a CA generation.

func CertURL

func CertURL(baseURL, caName string, generation int64) string

CertURL is the AIA "caIssuers" URL of a CA generation.

func CheckSANIndex

func CheckSANIndex(ctx context.Context, db pkidb.DBTX, limit int) (problems []string, checked int, err error)

CheckSANIndex compares certificate_sans with the names inside every certificate (restore check, PHASE1 step 16). Search, the expiry views and later ACME look names up in the index; a row that doesn't match its certificate would hide or invent a certificate. It returns one problem per certificate that doesn't match (at most limit) and the number checked.

func DeploymentNameSet added in v0.3.1

func DeploymentNameSet(names []string) (map[string]struct{}, error)

DeploymentNameSet normalizes the DNS names reported for an agent deployment.

func LegacyCRLURL

func LegacyCRLURL(baseURL, name string) string

LegacyCRLURL and LegacyCertURL are the URLs legacy pki.sh wrote into its certificates (AIA_BASE_URL/<name>.crl|.crt).

func LegacyCertURL

func LegacyCertURL(baseURL, name string) string

func NormalizeDNSName added in v0.2.0

func NormalizeDNSName(s string, allowWildcard bool) (string, error)

NormalizeDNSName checks a DNS name like a SAN and returns its canonical (lower-case) form – for ACME identifiers, which follow the same rules.

func SameACMENameSet added in v0.3.1

func SameACMENameSet(raw string, want map[string]struct{}) (bool, error)

SameACMENameSet compares a stored order with an already normalized set.

func SerialHex

func SerialHex(n *big.Int) string

SerialHex is the stored form of a serial: upper-case hex, even length (like OpenSSL's index.txt).

Types

type ACMEOrder added in v0.2.0

type ACMEOrder struct {
	OrderID, AccountID string
	// Stored runs inside the transaction that stores the certificate, so the
	// order and its certificate are written together (ADR-06).
	Stored func(ctx context.Context, tx *sql.Tx, issued *Issued) error
}

ACMEOrder ties an issuance to an ACME order.

type CAInfo

type CAInfo struct {
	ID, Name, Kind, Status, KeyMode string
	Parent                          string // name; "" for a root
	BaseURL                         string
	CurrentGeneration               int64 // 0 = none signed yet
	PathLen                         *int64
	CRLDays, MaxValidityDays        int64
	NameConstraints                 []string // "permitted dns homelab.lan"
	NameConstraintsCritical         bool
	EKULimit                        []string // names, or OIDs for unnamed ones
	Generations                     []GenerationInfo
}

CAInfo describes a CA for lists and detail views (read-only).

func ListCAs

func ListCAs(ctx context.Context, q pkidb.DBTX) ([]CAInfo, error)

ListCAs reads every CA of the tenant. q may be a read connection.

type CAResult

type CAResult struct {
	ID, Name, Status string
	Generation       int64
	CertDER          []byte // nil while the generation waits for its offline signature
	Fingerprint      string // SHA-256 of CertDER, upper-case hex
	Offline          *OfflineBundle
	CRL              *CRLResult // the first CRL of a generation whose key is on this server
	Notes, Warnings  []string
}

CAResult describes the CA after a change.

type CASettings

type CASettings struct {
	Name            string // immutable: part of every AIA/CRL URL
	BaseURL         string // public base URL for AIA/CRL; immutable once used
	CRLDays         int    // 0 = default for the kind
	MaxValidityDays int    // cap for leaves; 0 = 397
}

CASettings are what every CA has, whatever its kind.

type CRLResult

type CRLResult struct {
	CA                     string
	Generation             int64
	Number                 int64
	ThisUpdate, NextUpdate time.Time
	Entries                int
	DER                    []byte
}

CRLResult describes a published CRL.

type CRLStatus

type CRLStatus struct {
	CA                     string
	Generation             int64
	KeyMode                string
	Number                 int64
	Source                 string
	ThisUpdate, NextUpdate time.Time
	State                  string
	// Unpublished counts revocations not on the newest CRL yet: they take
	// effect only once a CRL lists them.
	Unpublished int
}

CRLStatus describes the newest CRL of one CA generation.

func CRLStatuses

func CRLStatuses(ctx context.Context, q pkidb.DBTX, now time.Time) ([]CRLStatus, error)

CRLStatuses reports every CA generation that needs a CRL (signed, and see crlNeededUntil). q may be a read connection.

func (CRLStatus) Message

func (s CRLStatus) Message(sealed bool) string

Message says what an admin should know, "" when all is well.

type CSRInspection

type CSRInspection struct {
	Subject             string
	CommonName          string
	DNSNames, IPs       []string
	Emails, URIs        []string
	KeyAlgorithm        string
	SignatureValid      bool
	RequestedExtensions []string
	Problems, Warnings  []string
}

CSRInspection is what a CSR asks for, parsed for a preview. Nothing is stored; the CSR's extensions are listed for information only.

func InspectCSR

func InspectCSR(data []byte) (*CSRInspection, error)

InspectCSR parses a CSR. A broken CSR is a DeniedError; a CSR whose key the policy refuses comes back with Problems.

type CertificateFilter

type CertificateFilter struct {
	CAID, Preset, Status  string
	HideAgentCertificates bool
	ExpiresBefore         time.Time
	// Search is a substring of CN, serial or a SAN – no wildcards.
	Search string
	Cursor string // the last ID of the previous page
	Limit  int    // 0 = DefaultPageSize; at most MaxPageSize
}

CertificateFilter selects certificates; every field is optional.

type CertificateInfo

type CertificateInfo struct {
	ID, Source, Status             string
	CAID, CAName                   string // "" for external certificates
	CAGeneration                   int64
	Preset                         string
	Serial, Subject, CommonName    string
	DNSNames, IPs, Emails, URIs    []string
	NotBefore, NotAfter, CreatedAt time.Time
	KeyAlgorithm, Fingerprint      string
	KeyStored                      bool
	RenewalOf, RequestedBy         string
	RevocationReason               string
	RevokedAt, InvalidityDate      *time.Time
	RevocationPlannedAt            *time.Time
	// contains filtered or unexported fields
}

CertificateInfo describes a stored certificate (read-only).

type CertificatePage

type CertificatePage struct {
	Items      []CertificateInfo
	NextCursor string // "" = last page
}

CertificatePage is one page of certificates, newest first.

type Counts

type Counts struct {
	Valid, Expiring, Expired, Revoked int64
}

Counts are certificate numbers by state; Valid includes Expiring.

func (Counts) Total

func (c Counts) Total() int64

Total is every certificate counted.

type DeniedError

type DeniedError struct {
	Reasons []string
	// DroppedCSRNames: CSR names the preset doesn't take ("type:value"),
	// reported even when the request is denied for another reason.
	DroppedCSRNames []string
	// Cause is the underlying refusal, e.g. authz.ErrStepUpRequired.
	Cause error
}

DeniedError lists every reason at once, so a requester can fix them all in one go. Reasons may contain requested names – escape them on output.

func (*DeniedError) Error

func (e *DeniedError) Error() string

func (*DeniedError) Unwrap

func (e *DeniedError) Unwrap() []error

Unwrap lets errors.Is find ErrDenied and the cause.

type Domain added in v0.2.0

type Domain struct {
	ID                string
	Name              string
	CA                string // the CA's name
	CAID              string
	IncludeSubdomains bool
	Wildcards         bool
	ChallengeTypes    []string
	Approval          string
	Preset            string
	ValidityDays      int // 0 = DefaultACMEValidityDays
	AddressPolicy     string
	IPRanges          []netip.Prefix
	CreatedAt         time.Time
	UpdatedAt         time.Time
}

Domain is a stored domain.

func (*Domain) AllowsChallenge added in v0.2.0

func (d *Domain) AllowsChallenge(t string) bool

AllowsChallenge reports whether d allows the challenge type t.

func (*Domain) Covers added in v0.2.0

func (d *Domain) Covers(identifierType, value string) bool

Covers reports whether d covers an identifier ("dns" or "ip"), with the domain's own rules for sub-domains, wildcards and IP ranges.

func (*Domain) EffectiveValidityDays added in v0.2.0

func (d *Domain) EffectiveValidityDays() int

EffectiveValidityDays is the validity of an ACME certificate under d.

type DomainRequest added in v0.2.0

type DomainRequest struct {
	Name string
	DomainSettings
	Who keys.Who
}

DomainRequest creates a domain.

type DomainSettings added in v0.2.0

type DomainSettings struct {
	CA                *string
	IncludeSubdomains *bool
	Wildcards         *bool
	ChallengeTypes    []string
	Approval          *string
	Preset            *string
	ValidityDays      *int // 0 = default
	AddressPolicy     *string
	IPRanges          []string
}

DomainSettings are the changeable parts of a domain. For a change, nil pointers and nil slices keep the current value; for a new domain they take the defaults.

type Engine

type Engine struct {
	PKI     *sql.DB // writer of tenants/<t>/pki.db
	Audit   *sql.DB // writer of tenants/<t>/audit.db: refused requests are logged directly
	Scope   string  // audit scope of Audit, store.TenantScope(tenant)
	Keys    *keys.Keyring
	Presets *preset.Set
	Clock   clock.Clock
	// ServerKeygen reports the tenant switch allow_server_keygen (system.db).
	// It is asked per request, so a change applies at once.
	ServerKeygen func(context.Context) (bool, error)
	// OtherPredecessor returns the tenant policy for non-agent renewals.
	OtherPredecessor func(context.Context) (predecessor.Policy, error)
	// Wake, if set, is called after a commit with an outbox row.
	Wake func()
	// Rand is the randomness for serials and keys; nil = crypto/rand.
	Rand io.Reader
	// Log, if set, gets CRL renewals and CRL warnings (process log).
	Log *slog.Logger
}

Engine issues certificates for one tenant.

func (*Engine) ApplyACMEPredecessor added in v0.3.1

func (e *Engine) ApplyACMEPredecessor(ctx context.Context, q *pkidb.Queries, tx *sql.Tx, accountID, successorID string,
	names map[string]struct{}, policy predecessor.Policy, who keys.Who) (revocations []*RevokeResult, ambiguous []string, err error)

ApplyACMEPredecessor handles every unambiguous older certificate of the same account with exactly the successor's name set. It never selects by hostname alone or across accounts.

func (*Engine) ApplyPredecessor added in v0.3.1

func (e *Engine) ApplyPredecessor(ctx context.Context, q *pkidb.Queries, tx *sql.Tx, certificateID, successorID string,
	policy predecessor.Policy, source string, who keys.Who, now time.Time) ([]*RevokeResult, error)

ApplyPredecessor revokes or plans one already authorized predecessor in the caller's pki.db transaction. Existing plans are preserved. The returned revocations still need CRL publication after the transaction commits.

func (*Engine) CA

func (e *Engine) CA(ctx context.Context, who keys.Who, id string) (*CAInfo, error)

CA returns one CA by ID.

func (*Engine) CAChain

func (e *Engine) CAChain(ctx context.Context, who keys.Who, id string) ([][]byte, error)

CAChain returns the certificate of the current generation of a CA and its issuers up to the root (DER, the CA first).

func (*Engine) CAName

func (e *Engine) CAName(ctx context.Context, who keys.Who, id string) (string, error)

CAName returns the name of the CA with this ID, if who may read it.

func (*Engine) CAs

func (e *Engine) CAs(ctx context.Context, who keys.Who) ([]CAInfo, error)

CAs lists the CAs who may read, with the CRL state of each generation.

func (*Engine) CancelRevocationPlan added in v0.3.1

func (e *Engine) CancelRevocationPlan(ctx context.Context, who keys.Who, certificateID string) error

CancelRevocationPlan removes only the pending plan.

func (*Engine) Certificate

func (e *Engine) Certificate(ctx context.Context, who keys.Who, id string) (*CertificateInfo, error)

Certificate returns one certificate by ID.

func (*Engine) CertificateChain

func (e *Engine) CertificateChain(ctx context.Context, who keys.Who, id string) (cert []byte, chain [][]byte, err error)

CertificateChain returns the certificate (DER) and the intermediates up to just below the root, issuing CA first – what a server sends. The root is the trust anchor the client already has: it doesn't belong into the chain (RFC 8446 §4.4.2; SSL Labs "chain contains anchor"); it is downloaded with CAChain. A certificate signed by the root itself has an empty chain.

func (*Engine) CertificateCounts

func (e *Engine) CertificateCounts(ctx context.Context, who keys.Who, filters ...CertificateFilter) (Counts, map[string]Counts, error)

CertificateCounts counts the certificates who may read, in total and per issuing CA ID. An optional filter excludes agent identity certificates.

func (*Engine) Certificates

func (e *Engine) Certificates(ctx context.Context, who keys.Who, f CertificateFilter) (*CertificatePage, error)

Certificates lists the certificates who may read.

func (*Engine) CheckManage

func (e *Engine) CheckManage(who keys.Who, name string) error

CheckManage tells whether who may manage the CA, a fresh step-up aside – for reading what only its managers see (the offline signing bundle).

func (*Engine) CreateDomain added in v0.2.0

func (e *Engine) CreateDomain(ctx context.Context, req DomainRequest) (*Domain, error)

CreateDomain stores a new domain.

func (*Engine) CreateIntermediate

func (e *Engine) CreateIntermediate(ctx context.Context, req IntermediateRequest) (*CAResult, error)

CreateIntermediate creates an intermediate CA. An online parent signs it at once; under an offline parent it waits for the signed certificate (InstallCACertificate) and the result carries the offline bundle.

func (*Engine) CreateRoot

func (e *Engine) CreateRoot(ctx context.Context, req RootRequest) (*CAResult, error)

CreateRoot creates an online root CA.

func (*Engine) DeleteDomain added in v0.2.0

func (e *Engine) DeleteDomain(ctx context.Context, id string, who keys.Who) error

DeleteDomain removes a domain. Certificates already issued stay; open ACME orders for its names fail at finalize, which checks the domain again.

func (*Engine) DomainFor added in v0.2.0

func (e *Engine) DomainFor(ctx context.Context, identifierType, value string) (*Domain, error)

DomainFor returns the most specific domain that covers a DNS name (a wildcard "*.x" counts as a name below x) or an IP address; nil without one.

func (*Engine) EffectiveOtherPredecessor added in v0.3.1

func (e *Engine) EffectiveOtherPredecessor(ctx context.Context) (predecessor.Policy, error)

EffectiveOtherPredecessor reads the tenant policy for UI, REST and ACME.

func (*Engine) ExecuteDueRevocations added in v0.3.1

func (e *Engine) ExecuteDueRevocations(ctx context.Context) error

ExecuteDueRevocations executes persisted plans. It is safe after a restart or unseal: stored revocations work while sealed and CRL publication catches up through the normal unseal/CRL jobs.

func (*Engine) Export

func (e *Engine) Export(ctx context.Context, req ExportRequest) (*Export, error)

Export builds the export file. It needs key.download (operator on the issuing CA, fresh second factor) and an unsealed server; the download is audited before the file leaves (fail-closed).

func (*Engine) ExportKey

func (e *Engine) ExportKey(ctx context.Context, who keys.Who, id string) ([]byte, error)

ExportKey returns the private key of a certificate with a server-generated key as unencrypted PKCS#8 PEM (PHASE1 step 13); the key stays on the server. Export has the other formats and the deletion (step 18).

func (*Engine) GetDomain added in v0.2.0

func (e *Engine) GetDomain(ctx context.Context, who keys.Who, id string) (*Domain, error)

GetDomain returns one domain; ErrNotFound when it doesn't exist or the caller may not read its CA (decision 64).

func (*Engine) ImportExternal

func (e *Engine) ImportExternal(ctx context.Context, certPEM []byte, who keys.Who) (*CertificateInfo, error)

ImportExternal stores a certificate of another CA (source external) for expiry monitoring; it needs tenant.manage. Nothing is checked against a policy – it is a record, not an issuance.

func (*Engine) ImportJobByID

func (e *Engine) ImportJobByID(ctx context.Context, who keys.Who, id string) (*ImportJob, error)

ImportJobByID reads a recorded job; it needs ca.manage (role only).

func (*Engine) ImportLegacy

func (e *Engine) ImportLegacy(ctx context.Context, req LegacyImportRequest) (*LegacyImportReport, error)

ImportLegacy checks and imports a pki.sh directory. Problems refuse the whole import (a DeniedError listing them); the report always comes back.

func (*Engine) ImportLegacyJob

func (e *Engine) ImportLegacyJob(ctx context.Context, req LegacyImportRequest) (*ImportJob, error)

ImportLegacyJob runs ImportLegacy and records the outcome. A caller who may not import gets the refusal and no job.

func (*Engine) ImportRoot

func (e *Engine) ImportRoot(ctx context.Context, req ImportRootRequest) (*CAResult, error)

ImportRoot imports an offline root certificate. For a new name it creates the CA; for an existing offline root it adds the next generation – the renewal of an offline root with a new key.

func (*Engine) InstallCACertificate

func (e *Engine) InstallCACertificate(ctx context.Context, name string, certPEM []byte, who keys.Who) (*CAResult, error)

InstallCACertificate takes the certificate the offline root signed and activates the waiting generation – after checking that it is exactly what was ordered.

func (*Engine) Issue

func (e *Engine) Issue(ctx context.Context, req Request) (*Issued, error)

Issue checks req, signs and stores the certificate. A *DeniedError (errors.Is ErrDenied) lists what the requester or an admin must change.

func (*Engine) LatestCRL

func (e *Engine) LatestCRL(ctx context.Context, who keys.Who, caID string) ([]byte, string, error)

LatestCRL returns the newest CRL of a CA's current generation (DER) for who may read the CA – the same file the public listener serves.

func (*Engine) ListDomains added in v0.2.0

func (e *Engine) ListDomains(ctx context.Context, who keys.Who) ([]Domain, error)

ListDomains returns the domains whose CA the caller may read, by name.

func (*Engine) OfflineBundleFor

func (e *Engine) OfflineBundleFor(ctx context.Context, name string) (*OfflineBundle, error)

OfflineBundleFor rebuilds the bundle of a CA generation that waits for its signature, e.g. to download it again.

func (*Engine) PlanRevocation added in v0.3.1

func (e *Engine) PlanRevocation(ctx context.Context, req PlanRevocationRequest) error

PlanRevocation schedules a certificate without changing its validity state.

func (*Engine) PublishPredecessorRevocations added in v0.3.1

func (e *Engine) PublishPredecessorRevocations(ctx context.Context, revocations []*RevokeResult, who keys.Who)

PublishPredecessorRevocations signs CRLs after the business transaction.

func (*Engine) RenewCA

func (e *Engine) RenewCA(ctx context.Context, req RenewRequest) (*CAResult, error)

RenewCA renews a CA with a new key: the next generation. An online root signs itself, an intermediate is signed by its parent – at once, or through the offline flow when the parent is offline. The old generation keeps its URL and goes on signing the CRL for its certificates (step 9). An offline root is renewed by importing its new certificate (ImportRoot).

func (*Engine) RenewCRLs

func (e *Engine) RenewCRLs(ctx context.Context, trigger string) ([]CRLResult, error)

RenewCRLs signs a new CRL for every generation with an online key whose CRL is missing, past a third of its lifetime or lacks a revocation. It runs on a timer and at unseal (wired with the server, step 12). While sealed it does nothing and returns keys.ErrSealed.

func (*Engine) RenewCertificate

func (e *Engine) RenewCertificate(ctx context.Context, req RenewCertRequest) (*Renewed, error)

RenewCertificate issues the successor through the normal issuance path (same checks and audit, renewal_of set). Its predecessor action is atomic with the new certificate; CRL signing follows the commit.

func (*Engine) RetireCA

func (e *Engine) RetireCA(ctx context.Context, name string, who keys.Who) error

RetireCA stops a CA from issuing. Its certificates stay valid and its CRLs go on until they have expired (step 9). It can't be undone.

func (*Engine) RevocationsDue added in v0.3.1

func (e *Engine) RevocationsDue(ctx context.Context, who keys.Who) (int64, error)

RevocationsDue counts pending plans through the next seven days that the caller may read, including overdue plans not yet executed.

func (*Engine) Revoke

func (e *Engine) Revoke(ctx context.Context, req RevokeRequest) (*RevokeResult, error)

Revoke revokes an end-entity certificate and signs the new CRL of its issuing generation.

func (*Engine) RevokeCA

func (e *Engine) RevokeCA(ctx context.Context, req RevokeCARequest) (*RevokeResult, error)

RevokeCA revokes a CA generation below an online parent and signs the parent's new CRL. A CA below an offline root is revoked on the root; its uploaded CRL then marks the generation (UploadCRL).

func (*Engine) SignCRL

func (e *Engine) SignCRL(ctx context.Context, caID string, generation int64, trigger string, who keys.Who) (*CRLResult, error)

SignCRL signs a new CRL for a CA generation whose key is on the server. It needs ca.manage; the CRL renewal runs as the system.

func (*Engine) UpdateCA

func (e *Engine) UpdateCA(ctx context.Context, req UpdateRequest) error

UpdateCA changes CRL lifetime and maximum certificate validity. Name and base URL never change: they are inside certificates already.

func (*Engine) UpdateDomain added in v0.2.0

func (e *Engine) UpdateDomain(ctx context.Context, id string, s DomainSettings, who keys.Who) (*Domain, error)

UpdateDomain changes a domain; the name never changes.

func (*Engine) UploadCRL

func (e *Engine) UploadCRL(ctx context.Context, name string, data []byte, who keys.Who) (*CRLResult, []string, error)

UploadCRL stores a CRL the offline root signed (owner decision 2026-10-14). It must be signed by exactly one generation of that root, its number must be higher than every CRL number of the CA so far, it must be current and a full CRL. Intermediates of ours that it lists as revoked are marked revoked at once – they stop issuing.

type Export

type Export struct {
	Data                  []byte
	ContentType, FileName string
	KeyDeleted            bool
}

Export is the file to hand out.

type ExportRequest

type ExportRequest struct {
	ID, Format string
	// Password protects a PKCS#12 file (at least pkcs12.MinPassword characters).
	Password string
	// DeleteKey destroys the key on the server after the export.
	DeleteKey bool
	Who       keys.Who
}

ExportRequest asks for a key export.

type GenerationInfo

type GenerationInfo struct {
	Generation       int64
	Current, Pending bool
	KeyAlgorithm     string
	Subject          string
	// Fingerprint is authoritative SHA-256; FingerprintSHA1 is display-only.
	Serial, Fingerprint string
	FingerprintSHA1     string
	NotBefore, NotAfter string
	Issuer              string // "root/1"; "" for a root
	CertURL, CRLURL     string // where MyOwnPKI publishes it
	// Warnings are found in the certificate itself, e.g. AIA/CDP URLs that
	// differ from what MyOwnPKI serves (an offline-signed certificate).
	Warnings []string
	// Revoked is "<reason> on <date>" for a revoked generation.
	Revoked string
	// CRL is the newest CRL; filled by AttachCRLStatus (it needs the time).
	CRL *CRLStatus
}

GenerationInfo is one key and certificate of a CA.

type ImportJob

type ImportJob struct {
	ID, Kind, Status   string
	DryRun             bool
	StartedAt, EndedAt time.Time
	StartedBy          string
	Report             *LegacyImportReport
	Error              string `json:"-"` // not stored apart: Report.Problems has it
}

ImportJob is a recorded import.

type ImportRootRequest

type ImportRootRequest struct {
	CASettings
	CertPEM []byte
	Who     keys.Who
}

ImportRootRequest imports the certificate of an offline root. A request for an existing offline root with a new key adds its next generation.

type IntermediateRequest

type IntermediateRequest struct {
	CASettings
	Parent          string
	Subject         Subject
	KeyAlgorithm    string // "" = ecdsa-p384
	ValidityDays    int    // 0 = 3650, never beyond the parent
	PathLen         int    // 0 = may only issue end-entity certificates
	NameConstraints NameConstraints
	EKULimit        []string // EKU names or OIDs; empty = no limit
	Who             keys.Who
}

IntermediateRequest creates an intermediate under Parent.

type Issued

type Issued struct {
	ID           string
	SerialHex    string
	Fingerprint  string // SHA-256, upper-case hex
	CertDER      []byte
	Chain        [][]byte // issuing CA first, root last
	KeyID        string   // server-generated key; "" for a CSR
	KeyAlgorithm string
	NotBefore    time.Time
	NotAfter     time.Time
	// Notes say what a default changed (validity shortened, base domain added).
	Notes []string
	// DroppedCSRNames are names from the CSR the preset doesn't take, as
	// "type:value" – shown to the requester and written to the audit log.
	DroppedCSRNames []string
	// Warnings are compatibility problems that didn't stop the issuance.
	Warnings []string
}

Issued is a stored certificate.

type LegacyCAReport

type LegacyCAReport struct {
	Name, Kind, KeyMode, Status string
	ID                          string `json:"id,omitempty"`
	Subject, Fingerprint        string
	Certificates, Revoked       int
	// LastCRLNumber is the newest number pki.sh used; the next CRL gets +1.
	LastCRLNumber int64
	History       int // older certificates of the same key
}

LegacyCAReport describes one CA of the import.

type LegacyImportReport

type LegacyImportReport struct {
	DryRun       bool             `json:"dry_run"`
	Imported     bool             `json:"imported"`
	BaseURL      string           `json:"base_url"`
	LegacyURLs   bool             `json:"legacy_urls"`
	CAs          []LegacyCAReport `json:"cas"`
	Certificates int              `json:"certificates"`
	Revoked      int              `json:"revoked"`
	Keys         int              `json:"keys"`
	External     int              `json:"external"`
	Warnings     []string         `json:"warnings"`
	Problems     []string         `json:"problems"`
	CRLs         []CRLResult      `json:"-"`
	// contains filtered or unexported fields
}

LegacyImportReport is what was (or, in a dry run, would be) imported.

func (*LegacyImportReport) CRLDaysOr

func (r *LegacyImportReport) CRLDaysOr(def, conf int) int

CRLDaysOr picks the CRL lifetime: pki.conf's CRL_DAYS, else the default.

type LegacyImportRequest

type LegacyImportRequest struct {
	PKI *legacy.PKI
	// BaseURL for the AIA/CRL URLs of new certificates; "" = AIA_BASE_URL of
	// pki.conf. With the pki.sh URL, <base>/<name>.crt|.crl stay served.
	BaseURL string
	// RootOnline imports the root key encrypted (default: an offline root –
	// pki.sh stays the offline root tool).
	RootOnline bool
	// DryRun checks everything and writes nothing.
	DryRun bool
	Who    keys.Who
}

LegacyImportRequest imports a directory read by legacy.Read.

type NameConstraints

type NameConstraints struct {
	PermittedDNS, ExcludedDNS     []string
	PermittedIP, ExcludedIP       []string
	PermittedEmail, ExcludedEmail []string
	PermittedURI, ExcludedURI     []string
	// NotCritical writes the extension non-critical – a compatibility switch
	// for old clients; RFC 5280 requires critical.
	NotCritical bool
}

NameConstraints limit the names a CA may issue for (RFC 5280 §4.2.1.10). DNS and URI values: "example.com" covers the domain and everything below it, ".example.com" only what is below. IP values are CIDR prefixes (IPv4 and IPv6). E-mail values: a mailbox, a host or ".domain".

type Names

type Names struct {
	DNS    []string
	IPs    []netip.Addr
	Emails []string
	URIs   []string
}

Names are the checked subject alternative names of one certificate.

type OfflineBundle

type OfflineBundle struct {
	CSRPEM    []byte
	ExtConfig string // contents of the extension file
	// Suggested file names: <name>-<generation>.csr / .ext / .crt.
	CSRFile, ExtFile, CertFile string
	Days                       int    // -days for the signature; ends before the parent does
	Digest                     string // matches the root key: sha384 for P-384; "" for Ed25519
	Serial                     string // random 159-bit serial for plain OpenSSL (-set_serial)
	Instructions               string
}

OfflineBundle is what the admin takes to the offline root.

type PlanRevocationRequest added in v0.3.1

type PlanRevocationRequest struct {
	CertificateID string
	PlannedAt     time.Time
	Who           keys.Who
}

PlanRevocationRequest schedules a final superseded revocation.

type RenewCertRequest

type RenewCertRequest struct {
	ID string
	// CSR is required when the original came from a CSR.
	CSR []byte
	// KeyAlgorithm of a new server key; "" = the original's.
	KeyAlgorithm string
	ValidityDays int
	// RevokePrevious is the legacy tri-state pointer: true = now, false = never,
	// nil = tenant default. Predecessor wins when both are present.
	RevokePrevious *bool
	Predecessor    *predecessor.Policy
	Who            keys.Who
}

RenewCertRequest asks for a successor of a certificate: same CA, preset, subject and names; always a new key – a server-generated one for a server-generated original, a new CSR otherwise.

type RenewRequest

type RenewRequest struct {
	Name         string
	KeyAlgorithm string // "" = the current algorithm (if allowed for new CAs)
	ValidityDays int
	Who          keys.Who
}

RenewRequest renews a CA with a new key: a new generation.

type Renewed

type Renewed struct {
	*Issued
	Revoked   *RevokeResult
	PlannedAt *time.Time
}

Renewed is the successor and, for an immediate choice, its predecessor revocation.

type Request

type Request struct {
	CA     string
	Preset string
	// CSR is a PEM certificate request. nil = generate the key on the server.
	CSR []byte
	// KeyAlgorithm for server-side generation; "" = DefaultKeyAlgorithm.
	KeyAlgorithm string

	DNSNames, IPAddresses, EmailAddresses, URIs []string
	// IgnoreCSRNames uses only the names given here and ignores the CSR's
	// names and subject (legacy --no-csr-san).
	IgnoreCSRNames bool
	Subject        Subject
	// SubjectDefaults are the tenant's (O, OU, L, ST, C), the lowest priority.
	SubjectDefaults Subject

	ValidityDays int // 0 = the preset's default
	// CustomKeyUsage and CustomExtKeyUsage: only for customizable presets.
	CustomKeyUsage, CustomExtKeyUsage []string
	RenewalOf                         string // certificate ID, optional
	Who                               keys.Who
	// ACME, if set, makes this the issuance of an ACME order: every name
	// must lie in a domain, and CA, preset and validity come from the
	// domains (PHASE2 step 2) – one policy engine for UI, API and ACME.
	ACME *ACMEOrder
	// Stored, if set, runs inside the transaction that stores the
	// certificate – the agent service links an agent to its certificate
	// this way, so neither exists without the other (ADR-06).
	Stored func(ctx context.Context, tx *sql.Tx, issued *Issued) error
	// contains filtered or unexported fields
}

Request is one certificate request. Every field is untrusted input.

type RevokeCARequest

type RevokeCARequest struct {
	Name           string
	Generation     int64 // 0 = the current generation
	Reason         string
	InvalidityDate *time.Time
	Who            keys.Who
}

RevokeCARequest revokes a CA generation signed by an online parent.

type RevokeRequest

type RevokeRequest struct {
	CertificateID string
	Reason        string
	// InvalidityDate: when the key is known or suspected to be compromised
	// (keyCompromise only); nil = not given.
	InvalidityDate *time.Time
	Who            keys.Who
}

RevokeRequest revokes an end-entity certificate.

type RevokeResult

type RevokeResult struct {
	Serial    string
	CRL       *CRLResult // nil when no CRL could be signed yet
	Published bool       // false: stored, but not on a CRL yet (sealed server)
	Warnings  []string
	// contains filtered or unexported fields
}

RevokeResult says whether the revocation is already published.

type RootRequest

type RootRequest struct {
	CASettings
	Subject      Subject
	KeyAlgorithm string // "" = ecdsa-p384
	ValidityDays int    // 0 = 7300
	Who          keys.Who
}

RootRequest creates an online root: its key is encrypted in pki.db.

type Subject

type Subject struct {
	CommonName         string
	Organization       string
	OrganizationalUnit string
	Locality           string
	Province           string
	Country            string
}

Subject holds the distinguished name fields a request may set. Everything else in a CSR's subject is ignored.

type UpdateRequest

type UpdateRequest struct {
	Name            string
	CRLDays         int
	MaxValidityDays int
	Who             keys.Who
}

UpdateRequest changes the limits of a CA; zero keeps a value.

Directories

Path Synopsis
Package legacy reads a PKI directory of legacy/pki.sh (PHASE1 step 15): pki.conf, the root and intermediate CAs (ca.meta, certs/ca.crt, private/ca.key, index.txt, newcerts/, crlnumber, crl/ca.crl, archive/) and the issued certificates (issued/<name>/cert.meta, cert.pem, key.pem, archive/).
Package legacy reads a PKI directory of legacy/pki.sh (PHASE1 step 15): pki.conf, the root and intermediate CAs (ca.meta, certs/ca.crt, private/ca.key, index.txt, newcerts/, crlnumber, crl/ca.crl, archive/) and the issued certificates (issued/<name>/cert.meta, cert.pem, key.pem, archive/).
legacytest
Package legacytest writes a small directory in the format of legacy/pki.sh with Go only – for tests that must run without bash and OpenSSL (Windows, macOS).
Package legacytest writes a small directory in the format of legacy/pki.sh with Go only – for tests that must run without bash and OpenSSL (Windows, macOS).
Package preset loads and validates certificate presets – profiles such as "server" or "smime" that decide which key usages, extended key usages and subject alternative names an end-entity certificate gets.
Package preset loads and validates certificate presets – profiles such as "server" or "smime" that decide which key usages, extended key usages and subject alternative names an end-entity certificate gets.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL