The life cycle of a certificate: * Generating a key pair. * Generating a CSR containing public key. * Submitting CSR to a CA. * Checking for response from CA. * Waiting for certificate to near expiration. Administrative action can also add these states: * On hold * Revoked We model that life cycle as a state machine. Now with some arbitrarily-named states for our per-certificate state machine: * Generating a key pair. States: NEED_KEY_PAIR, GENERATING_KEY_PAIR [*], NEED_KEY_GEN_TOKEN, NEED_KEY_GEN_PIN, HAVE_KEY_PAIR * Reading info about key pair. NEED_KEYINFO, READING_KEYINFO [*], NEED_KEYINFO_READ_TOKEN, NEED_KEYINFO_READ_PIN, HAVE_KEYINFO * Generating a CSR containing public key. States: NEED_CSR, GENERATING_CSR [*], NEED_CSR_GEN_TOKEN, NEED_CSR_GEN_PIN, HAVE_CSR * Submitting CSR to a CA. States: NEED_TO_SUBMIT, SUBMITTING [*] * Don't know which CA to submit to. States: NEED_CA [*] * Don't have complete information about CA. States: CA_UNCONFIGURED [*] * Can't contact CA. States: CA_UNREACHABLE [*] * Rejected, very sad. States: CA_REJECTED [*] * CA is thinking. States: CA_WORKING [*] * Saving certificate to the desired location and parsing it for the information we think is interesting. States: NEED_TO_SAVE_CERT, PRE_SAVE_CERT[*], START_SAVING_CERT, SAVING_CERT [*], NEED_TO_READ_CERT, READING_CERT [*], SAVED_CERT, POST_SAVED_CERT[*] * Waiting for certificate to near expiration. States: MONITORING * Notifying the admin of impending/passed expiration. States: NEED_TO_NOTIFY, NOTIFYING [*] * Waiting for user input States: NEED_GUIDANCE [*] * Getting our bearings States: NEWLY_ADDED, NEWLY_ADDED_READING_KEYINFO [*], NEWLY_ADDED_NEED_KEYINFO_READ_TOKEN, NEWLY_ADDED_NEED_KEYINFO_READ_PIN, NEWLY_ADDED_START_READING_CERT, NEWLY_ADDED_READING_CERT [*], NEWLY_ADDED_DECIDING [*] [*] Denotes states in which we have to wait for instructions from the user or completion of interaction with external systems. State logic: NEED_KEY_PAIR: start-key-generation state_next = GENERATING_KEY_PAIR state_transition = now break GENERATING_KEY_PAIR: if starting-up state_next = NEED_KEY_PAIR state_transition = now else if keygen-finished if key-was-stored-successfully state_next = HAVE_KEY_PAIR state_transition = now elseif key-store-needs-token state_next = NEED_KEY_GEN_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEED_KEY_GEN_PIN state_transition = now else state_next = NEED_KEY_PAIR state_transition = now else state_next = GENERATING_KEY_PAIR state_transition = when-notified break NEED_KEY_GEN_TOKEN: if starting-up state_next = NEED_KEY_PAIR state_transition = soon break NEED_KEY_GEN_PIN: if starting-up state_next = NEED_KEY_PAIR state_transition = now break HAVE_KEY_PAIR: state_next = NEED_KEYINFO state_transition = now break NEED_KEYINFO: start-reading-key-information state_next = READING_KEYINFO state_transition = now break READING_KEYINFO: if starting-up state_next = NEED_KEYINFO state_transition = now else if finished-reading-key-information state_next = HAVE_KEYINFO state_transition = now elseif key-store-needs-token state_next = NEED_KEYINFO_READ_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEED_KEYINFO_READ_PIN state_transition = now else state_next = NEED_KEY_PAIR state_transition = now break NEED_KEYINFO_READ_TOKEN: if starting-up state_next = NEED_KEYINFO state_transition = soon break NEED_KEYINFO_READ_PIN: if starting-up state_next = NEED_KEYINFO state_transition = soon break HAVE_KEYINFO: state_next = NEED_CSR state_transition = now break NEED_CSR: if starting-up state_next = HAVE_KEYINFO state_transition = now else if don't-have-a-full-template fill-in-template-values-based-on-defaults start-csr-generation-using-template-values state_next = GENERATING_CSR state_transition = now break GENERATING_CSR: if starting-up state_next = HAVE_KEYINFO state_transition = now else if csrgen-finished if csr-was-stored state_next = HAVE_CSR state_transition = now elseif key-store-needs-token state_next = NEED_CSR_GEN_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEED_CSR_GEN_PIN state_transition = now else state_next = NEED_CSR state_transition = now else state_next = GENERATING_CSR state_transition = when-notified break NEED_CSR_GEN_TOKEN: if starting-up state_next = HAVE_KEYINFO state_transition = soon break NEED_CSR_GEN_PIN: if starting-up state_next = HAVE_KEYINFO state_transition = now break HAVE_CSR: state_next = NEED_TO_SUBMIT state_transition = now break NEED_TO_SUBMIT: if starting-up state_next = HAVE_CSR state_transition = now else start-csr-submission if csr-submission-started state_next = SUBMITTING state_transition = now else if don't-know-a-ca state_next = NEED_CA state_transition = now break SUBMITTING: if starting-up state_next = HAVE_CSR state_transition = now else if csr-submission-attempt-completed if ca-issued-cert state_next = NEED_TO_SAVE_CERT state_transition = now elseif ca-rejected-us if already-had-a-cert state_next = MONITORING state_transition = now else state_next = CA_REJECTED state_transition = later elseif ca-is-unreachable store-ca-cookie state_next = CA_UNREACHABLE state_transition = later elseif ca-is-thinking-about-it-and-have-cookie store-ca-cookie state_next = CA_WORKING state_transition = soon elseif ca-is-underconfigured if already-had-a-cert state_next = MONITORING state_transition = now else store-ca-cookie state_next = CA_UNCONFIGURED state_transition = later else state_next = NEED_GUIDANCE state_transition = now else state_next = SUBMITTING state_transition = when-notified break NEED_TO_SAVE_CERT: if pre-save-command-configured start-configured-pre-save-command state_next = PRE_SAVE_CERT state_transition = now else state_next = START_SAVING_CERT state_transition = now break PRE_SAVE_CERT: if starting-up state_next = NEED_TO_SAVE_CERT state_transition = now else if pre-save-completed state_next = START_SAVING_CERT state_transition = now break START_SAVING_CERT: start-saving-cert state_next = SAVING_CERT state_transition = now break SAVING_CERT: if starting-up state_next = NEED_TO_SAVE_CERT state_transition = now else if cert-save-completed state_next = NEED_TO_READ_CERT state_transition = now break NEED_TO_READ_CERT: start-reading-cert state_next = READING_CERT state_transition = now break READING_CERT: if starting-up state_next = NEED_TO_READ_CERT state_transition = now else if cert-read-completed state_next = SAVED_CERT state_transition = now break SAVED_CERT: if post-save-command-configured start-configured-post-save-command state_next = POST_SAVED_CERT state_transition = now else state_next = MONITORING state_transition = now break POST_SAVED_CERT: if starting-up state_next = SAVED_CERT state_transition = now else if post-save-completed state_next = MONITORING state_transition = now CA_REJECTED: state_transition = soon break CA_WORKING: if starting-up state_next = HAVE_CSR state_transition = now else state_next = NEED_TO_SUBMIT state_transition = soon break CA_UNREACHABLE: if starting-up state_next = HAVE_CSR state_transition = now else state_next = NEED_TO_SUBMIT state_transition = soon break CA_UNCONFIGURED: if starting-up state_next = HAVE_CSR state_transition = now break NEED_CA: if starting-up state_next = HAVE_CSR state_transition = now break NEED_GUIDANCE: if have-guidance state_next = as-guided state_waitfor = now else state_next = NEED_GUIDANCE state_transition = timeout break MONITORING: if certificate-is-expired or (expiration-time-is-below-notify-threshold-value and expiration-time-was-above-notify-threshold-value) update-template-values-based-on-cert state_next = NEED_TO_NOTIFY state_transition = now else if (expiration-time-is-below-renewal-threshold-value and expiration-time-was-above-renewal-threshold-value) state_next = NEED_CSR state_transition = now else state_next = MONITORING state_transition = timeout break NEED_TO_NOTIFY: if starting-up state_next = MONITORING state_transition = now else start-notifying state_next = NOTIFYING state_transition = now break NOTIFYING: if starting-up state_next = NEED_TO_NOTIFY state_transition = now else if notification-completed if this-cert-gets-autorenew and (expiration-time-is-below-renewal-threshold-value and expiration-time-was-above-renewal-threshold-value) state_next = NEED_CSR state_transition = now else state_next = MONITORING state_transition = timeout break NEWLY_ADDED: if key-storage-is-known state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now else state_next = NEWLY_ADDED_START_READING_CERT state_transition = now break NEWLY_ADDED_START_READING_KEYINFO: start-reading-key-information state_next = NEWLY_ADDED_READING_KEYINFO state_transition = now break NEWLY_ADDED_READING_KEYINFO: if starting-up state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now else if finished-reading-key-information state_next = NEWLY_ADDED_START_READING_CERT state_transition = now elseif key-store-needs-token state_next = NEWLY_ADDED_NEED_KEYINFO_READ_TOKEN state_transition = now elseif key-store-needs-pin state_next = NEWLY_ADDED_NEED_KEYINFO_READ_PIN state_transition = now else state_next = NEWLY_ADDED_START_READING_CERT state_transition = now break NEWLY_ADDED_NEED_KEYINFO_READ_TOKEN: if starting-up state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now break NEWLY_ADDED_NEED_KEYINFO_READ_PIN: if starting-up state_next = NEWLY_ADDED_START_READING_KEYINFO state_transition = now break NEWLY_ADDED_START_READING_CERT: start-reading-cert state_next = NEWLY_ADDED_READING_CERT state_transition = now break NEWLY_ADDED_READING_CERT: if starting-up state_next = NEWLY_ADDED_START_READING_CERT state_transition = now else if finished-reading-cert state_next = NEWLY_ADDED_DECIDING state_transition = now break NEWLY_ADDED_DECIDING: if entry-has-no-associated-ca try-to-set-ca-using-known-ca-list if we-have-a-cert state_next = MONITORING state_transition = now else if key-storage-is-known if key-is-present state_next = NEED_CSR state_transition = now else state_next = NEED_KEY_PAIR state_transition = now else state_next = NEED_GUIDANCE state_transition = now break Types of actions and user guidance: Reenroll-from-scratch ("request -g"): state_next = NEED_KEY_PAIR state_transition = now Submit-key ("request", if no csr or arguments alter it): state_next = NEED_CSR state_transition = now Submit-csr (automatic for "request") state_next = HAVE_CSR state_transition = now Resubmit-csr (need to add "resubmit") state_next = HAVE_CSR state_transition = now Start-Tracking-with-AutoRenew ("start-tracking"): add-cert-to-monitoring-list state_next = MONITORING state_transition = now Start-Tracking-without-AutoRenew ("start-tracking"): add-cert-to-monitoring-list state_next = MONITORING state_transition = now Cancel ("stop-tracking"): remove-cert-from-monitoring-list Status ("list"): dump-monitoring-list Data we need to track for each certificate/task/dbentry: * Type of key pair to generate [or use default settings] default: RSA,2048 * Location of key pair [use-once default] default: NSS,/etc/pki/nssdb,,Server-Key-default * Location of certificate [use-once default] default: NSS,/etc/pki/nssdb,,Server-Cert-default * Cached certificate issuer/serial/subject/spki/expiration/host/email * The last time we checked if expiration was imminent. * Interesting TTL values [or use default settings] default: 30*24*60*60,7*24*60*60,3*24*60*60,2*24*60*60,1*24*60*60 * How to notify administrator [or use default settings] syslog(LOG_AUTHPRIV?) or mail to root@? * CSR template information [or imported from existing certificate] * subject (cn=host name) * SANs¹ * DNS * email * principal name * ku¹, eku¹ ¹ Encoded as extensionRequest attributes. * Certificate State (state_current) * Whether to autorenew-at-expiration [or use default settings] * Whether to start monitoring at issue [or use default settings] * Type and location of CA [or use default settings] * Value of CA cookie for in-progress submissions. * Date of submission for in-progress submissions.