Skip to content

Certificate authorities

PKIFactor does not sign certificates itself: it acts as a registration authority in front of your certificate authorities (CAs). This page covers the private and enterprise CAs: HashiCorp Vault PKI, Microsoft AD CS, EJBCA and other REST CAs, and CAs reachable only through CMP. Public CAs (DigiCert, Sectigo, ACME CAs) are on Public CAs and domain validation.

Three objects

Object Menu Holds
Server Administration workspace, Infrastructure > Servers Where the CA is (host, base URL) and how PKIFactor authenticates (credentials, encrypted at rest)
PKI connector Infrastructure > PKI Connectors The CA type, the server it uses, and the CA-specific settings
Certificate template Connector menu Certificate Profiles (page Certificate Templates) What the CA offers: Vault roles, AD CS templates, EJBCA certificate profiles. Synchronized from the CA, or created by hand

A certificate profile then binds one connector and one of its templates, and adds PKIFactor's own rules (algorithms, subject, approval, protocols).

From the CA server to the device

PKI Connectors

Supported CA types

The Type list of the connector form shows the raw type names:

Type Certificate authority Server authentication method
VAULT HashiCorp Vault PKI secrets engine APPROLE or mTLS
ADCS Microsoft AD CS, agentless over WinRM (PowerShell remoting) WINRM
GENERIC Any REST CA through a preset: ZetaCA, EJBCA, Step CA, Keyfactor Command, or Universal REST (your own definition) Depends on the CA (mTLS for EJBCA)
CMP_CA A CA reachable only through CMP (RFC 4210, RFC 9483) CMP_SIGNATURE or CMP_PBM
DIGICERT, DIGICERT_ONE, SECTIGO, SECTIGO_SCM, PUBLIC_ACME Public CAs See Public CAs and domain validation

Add a CA

1. Declare the server

Infrastructure > Servers > Add Server:

Field Value
Server Name A name of your choice
Host / URL Host name or address of the CA server
Base URL Required for REST CAs (Vault, EJBCA and other GENERIC): the full URL, for example https://vault.example.com:8200. Not used by AD CS
Authentication Method The method; the form then shows its fields (see each CA below)

Secrets are encrypted before they are stored and are never displayed again. Change them under Manage Credentials. For a Vault AppRole credential, Rotate generates a new secret ID (this needs the AppRole Role Name (for rotation) field).

2. Create the connector

Infrastructure > PKI Connectors > Add PKI Connector: Connector Name, Server, Type, then the fields of that type (described below). Two optional fields exist for every type:

Field Use
Outbound proxy Empty: inherit the proxy of the environment. direct: never use a proxy (typical for an internal CA). A URL such as http://proxy:3128: this CA only goes through that proxy
CA Chain (PEM) Intermediate and root certificates, used as a fallback when the chain cannot be read from the CA

The edit page adds Allowed internal networks (revocation): comma-separated internal CIDRs that PKIFactor may contact to check the CRL or OCSP of this internal CA. Empty means only public addresses are checked (protection against server-side request forgery); loopback and cloud metadata addresses stay blocked in all cases.

The edit page also holds the SCEP RA Keypair panel, used when a profile on this connector is enabled for SCEP: Generate self-signed, Rotate and Import… (a CA-issued RA certificate and its RSA key). See SCEP.

The connector list shows, for each connector, its Type, Server, CA Name, Status and Certificates issued. Deleting a connector asks for confirmation; the profiles that use it can no longer issue.

3. Get the templates

Open the connector menu (⋮) > Certificate Profiles. On the Certificate Templates page:

  • CA profiles shows, read only and queried live, the profiles the CA declares and whether each one is already imported (In PKIFactor). Nothing is created or changed; it is a quick way to see what Sync From PKI would bring.
  • Sync From PKI reads the templates from the CA and detects the CA name (shown in the CA Name column of the connector list). It is also the simplest way to prove that address, credentials and rights are correct.
  • Create Manually adds a template the CA cannot list (for example when the EJBCA profile service is disabled). The Template Name must be the exact name on the CA; Display Name, Validity (days) (empty means 365 days) and Key Algorithms are optional.
  • Edit corrects the display name and the validity requested from the CA. The technical name cannot be changed. Some CAs ignore the requested validity and apply their own (AD CS always applies its template; Vault caps at the role's max_ttl). PKIFactor compares the validity obtained with the validity requested and logs a warning when they differ.
  • A template still used by a certificate profile cannot be deleted: rebind the profiles first.

4. Import what the CA already issued (optional)

From the connector details (⋮ > Details), Absorb Certificates imports the certificates the CA has already issued (Vault, AD CS, EJBCA and other CAs that can list certificates; not CMP, and not offered for DigiCert CertCentral and the Sectigo reseller connector). The dialog takes Max certificates to import, Issued after (optional), Skip revoked certificates and Skip certificates expired for more than (days) (0 keeps everything), and shows a Preview before you Confirm import.

Import from file imports PEM, DER, P7B or CRT files instead (up to 50 files, 10 MB each). Bundles are split into individual certificates, and duplicates (same SHA-256 fingerprint) are skipped. A bulk inventory import from a CSV file is available through the API (below).

Check that a connector works

The Status column of the connector list shows the result of the last connection test: Connected, Test failed (with its probable cause: product, third-party device or service, network, configuration, invalid input) or Not tested. Opening the list never contacts the CAs. To run a test, open the connector's action menu and choose Test connection (since version 1.8.50; before, the test was available through the API only): the list then shows the new status.

In this version the connection test is run through the API (below); in the interface, Sync From PKI exercises the same address, credentials and rights.

Type What the test does
Vault Authenticates, then reads /v1/sys/health (degraded when Vault is sealed or on standby)
AD CS Opens a WinRM session and runs a command on the CA host
EJBCA GET /ejbca/ejbca-rest-api/v1/certificate/status with the client certificate
CMP Sends an empty message; success means the endpoint answered HTTP 200 with the CMP media type (reachability only, not authentication)
Other GENERIC The test_connection endpoint of the preset
Via the API

curl -s -c jar -X POST https://clm.example.com/api/auth/login \
  --data-urlencode "email=admin@example.com" --data-urlencode "password=$PASSWORD"
curl -s -b jar https://clm.example.com/api/v1/inventory/pkis              # connectors
curl -s -b jar -X POST https://clm.example.com/api/v1/pkis/3/test-connection   # test, records the status (POST since 1.8.51)
curl -s -b jar -X POST https://clm.example.com/api/v1/pkis/3/sync-templates
curl -s -b jar https://clm.example.com/api/v1/pkis/3/templates            # what the CA offers

# Bulk inventory import from CSV (one line per certificate, common_name required)
curl -s -b jar -o template.csv https://clm.example.com/api/v1/pkis/import-inventory-csv/template
curl -s -b jar -X POST -F "file=@inventory.csv" https://clm.example.com/api/v1/pkis/3/import-inventory-csv
The test answers status (ok, degraded or error) and a message. Secrets in connector_config are always returned masked.

HashiCorp Vault PKI

Tested

Tested against a Vault PKI secrets engine. The automatic rotation of the AppRole secret ID is not covered by this statement.

One connector targets one issuer of one PKI mount. To use several issuers, create several connectors. Each Vault role becomes a template: the role remains the authority on allowed names, key types, usages and maximum lifetime; a profile can never exceed what its role allows.

Prepare Vault

vault secrets enable -path=pki-int pki
vault write pki-int/roles/srv-tls allowed_domains="example.com" allow_subdomains=true \
  key_type=rsa key_bits=2048 max_ttl="8760h"

A least-privilege policy for PKIFactor (adapt the mount name):

path "pki-int/issue/*"       { capabilities = ["create", "update"] }
path "pki-int/sign/*"        { capabilities = ["create", "update"] }
path "pki-int/revoke"        { capabilities = ["create", "update"] }
path "pki-int/roles"         { capabilities = ["list"] }
path "pki-int/roles/*"       { capabilities = ["read"] }
path "pki-int/issuer/*/json" { capabilities = ["read"] }
path "pki-int/certs"         { capabilities = ["list"] }
path "pki-int/cert/*"        { capabilities = ["read"] }
path "pki-root/ca/pem"       { capabilities = ["read"] }   # only if the root lives on another mount
path "auth/token/renew-self" { capabilities = ["update"] }

Attach it to an AppRole role, or to a cert auth role for mTLS:

# AppRole
vault auth enable approle
vault write auth/approle/role/pkifactor-pki token_policies="pkifactor-pki" token_ttl=1h token_max_ttl=4h
# or mTLS: PKIFactor presents a client certificate issued by the trusted CA
vault auth enable cert
vault write auth/cert/certs/pkifactor certificate=@trust-ca.pem \
  allowed_common_names="pkifactor-clm" token_policies="pkifactor-pki"

A role with no_store=true issues certificates that Vault does not keep: they cannot be absorbed.

Declare it in PKIFactor

Server: Base URL in HTTPS, then one of:

Method Fields
APPROLE Role ID, Secret ID, PKI mount point (default pki), Issuer reference (issuer ID or name, or default), optionally AppRole Role Name (for rotation) and Auto-rotate Secret ID (every 6h)
mTLS Client Certificate (PEM), Private key (PEM), optionally CA Chain (PEM) and Auth Role Name (the cert role, for example pkifactor)

Then a connector of type VAULT on that server, and Sync From PKI.

The issuer reference is mandatory: without it the connector refuses to start ("Vault must target a single CA: 'issuer_ref' is required"). With mTLS, the credential form has no mount or issuer field and the connector form has no Vault section: set mount_point and issuer_ref in the connector configuration through the API.

Via the API: Vault connector with mTLS
curl -s -b jar -X POST https://clm.example.com/api/v1/inventory/pkis \
  -H "Content-Type: application/json" \
  -d '{"name":"Vault PKI","server_id":5,"pki_type":"VAULT",
       "connector_config":{"mount_point":"pki-int","issuer_ref":"default"}}'

Microsoft AD CS

Tested

Tested live over WinRM with a dedicated service account that has no administrative rights: template synchronization, CA detection, issuance, revocation and absorption, with CredSSP authentication. NTLM and Kerberos authentication were not tested.

The connector is agentless: it opens a PowerShell remoting session (WinRM, HTTPS port 5986, CredSSP by default) and uses the CA's COM interfaces for issuance, revocation and inventory.

Least-privilege service account

Never use a domain administrator or a local administrator of the CA, and do not grant Manage CA.

Where Right Unlocks
CA host, local group Remote Management Users Membership WinRM session and COM activation
certsrv.msc > CA properties > Security Request Certificates Issuance
certsrv.msc > CA properties > Security Issue and Manage Certificates Revocation and inventory (without it: ACCESS_DENIED (0x5))
certtmpl.msc > each template exposed to PKIFactor > Security Read and Enroll Synchronization and issuance on that template

Restart the AD CS service after changing the CA security. A plain domain user (Domain Users) is enough otherwise.

Templates: "Supply in the request"

PKIFactor builds the request (subject and SANs validated by the profile), so each template must use Supply in the request on its Subject Name tab. With Build from Active Directory information, the CA ignores the request and issues the certificate in the name of the service account. Do not enable EDITF_ATTRIBUTESUBJECTALTNAME2 on the CA. A template that requires CA manager approval leaves the request pending on the CA side.

WinRM over HTTPS on the CA host

The WinRM listener needs a server certificate for the CA host name (Server Authentication usage), ideally issued by your internal CA and reserved for this listener. winrm quickconfig does not create one.

winrm create winrm/config/Listener?Address=*+Transport=HTTPS '@{Hostname="adcs.example.com";CertificateThumbprint="<thumbprint>"}'
Enable-WSManCredSSP -Role Server -Force
New-NetFirewallRule -Name "WinRM-HTTPS" -Direction Inbound -Protocol TCP -LocalPort 5986 -Action Allow
winrm enumerate winrm/config/listener

TLS verification of the WinRM listener

Since the security review of September 2026, PKIFactor verifies the listener certificate by default: with CredSSP, the service account password is delegated to the server, and an unverified channel would hand it to anyone able to intercept the connection. There is no CA field for AD CS: the listener certificate must chain to a CA trusted by the backend container. A laboratory with a self-signed listener must say so explicitly by setting verify_cert to the JSON boolean false in the connector configuration (API only).

Declare it in PKIFactor

Server: Host / URL = the CA host, method WINRM, Username (DOMAIN\svc-pkifactor) and Password; leave Base URL empty. Connector: type ADCS, then Sync From PKI: the CA name (HOST\CA name) is detected and the published templates appear. Issuance and revocation are refused as long as the CA name is unknown.

Optional settings, in the connector configuration (API): auth (credssp by default, or ntlm, kerberos), port (5986), use_ssl (true), domain (prefixed to the user name when it has none), verify_cert.

Via the API: connector configuration

curl -s -b jar -X PUT https://clm.example.com/api/v1/inventory/pkis/7 \
  -H "Content-Type: application/json" -d '{"connector_config":{"auth":"credssp","port":5986}}'
connector_config replaces the whole configuration of the connector: include every key you want to keep.

EJBCA

Tested

Tested against EJBCA Community (9.3.7) with mTLS and a least-privilege RA role: connection test, CA detection, issuance and revocation. EJBCA Enterprise was not tested. A revocation bug found in September 2026 (the default reason was sent as NOT_REVOKED, so EJBCA did not revoke) is fixed: the default reason is now UNSPECIFIED.

EJBCA uses a GENERIC connector with the EJBCA preset (REST API v1, client certificate authentication).

Prepare EJBCA

  • Enable REST Certificate Management in System Configuration > Protocol Configuration.
  • Create a dedicated client certificate for PKIFactor and bind it to a dedicated role (match on its CN or serial number). Rules proven sufficient to issue and revoke: /administrator (mandatory, otherwise every REST call gets 403), /ca/<issuing CA>, /ca_functionality/create_certificate, /ra_functionality/create_end_entity, revoke_end_entity, view_end_entity, and /endentityprofilesrules/<end entity profile>/ with the same three end-entity rights. Do not use the SuperAdmin certificate.
  • Use the issuing CA (not the management CA) and a certificate profile and end entity profile prepared for PKIFactor.

Declare it in PKIFactor

Server: Base URL (for example https://ejbca.example.com:8443), method mTLS with Client Certificate (PEM) and Private key (PEM). Connector: type GENERIC, Connector Preset EJBCA, then the four parameters:

Parameter Value
Certificate Authority Name The issuing CA name in EJBCA
End Entity Profile For example EMPTY or a dedicated profile
Issuer DN The exact, complete DN of the issuing CA (used in the revocation path; an approximate value breaks revocation)
End Entity Password The enrollment password (stored encrypted)

Templates are EJBCA certificate profiles. Their list comes from /v2/certificate/profiles, a service many Community instances disable ("This service has been disabled"); then use Create Manually with the exact profile name (for example SERVER). Issuance and revocation do not depend on that service.

The EJBCA preset verifies the server certificate by default since September 2026. For a laboratory EJBCA with a self-signed certificate, tick Skip TLS verification explicitly; never in production.

Other REST CAs (generic connector)

Type GENERIC with a Connector Preset: ZetaCA, EJBCA, Step CA, Keyfactor Command, or Universal REST for your own definition. A preset fills in the endpoints and asks only for its parameters; Advanced: endpoint overrides lets you change an endpoint. A custom definition must at least define the signing endpoint (endpoints.sign_csr). Authentication material stays on the server, never in the connector.

TLS verification is on by default (tls.verify_ssl). Skip TLS verification is meant for laboratories only.

CA reachable only through CMP

Type CMP_CA: PKIFactor speaks CMP (RFC 4210, RFC 9483 profile) to the upstream CA, for CAs that expose nothing else (telecom, automotive, industry).

Field Value
Endpoint URL The CMP endpoint of the CA (HTTP POST, application/pkixcmp)
Recipient DN Subject DN of the upstream CA
Sender DN Defaults to CN=PKIFactor
Protection Mode Leave Auth defined on the server (recommended): the protection comes from the server credential, CMP_SIGNATURE (RA certificate and key trusted by the CA) or CMP_PBM (shared secret, at least 16 characters)
Trust Anchor (PEM) The CA certificate used to verify the CA's responses and provide the chain
TLS verify, Timeout (s) On by default; 30 seconds

What to expect:

  • PKIFactor sends an initialization request with raVerified proof of possession: it vouches for the key as an RA. The CA must accept this from PKIFactor. With EJBCA, this works only through a CMP alias in RA mode with allowraverifypopo=true; that combination was proven against EJBCA. A CA that requires the end entity's own proof of possession refuses the request.
  • There is no template list: the CA decides the certificate profile and the validity from its own configuration (alias, recipient).
  • Revocation is supported; absorption is not (CMP has no inventory operation).

TLS verification of the CA endpoints

CA Default How to trust a private CA How to turn verification off (laboratory)
Vault Verified against the system CAs ca_bundle (or vault_ca_cert) in the connector configuration: the CA of Vault's server certificate, in PEM or as a file path readable by the backend skip_verify in the connector configuration
AD CS (WinRM) Verified (since September 2026) The listener certificate must chain to a CA trusted by the backend container verify_cert: false
EJBCA and generic REST Verified (EJBCA preset since September 2026) tls.ca_bundle: path of a CA file readable by the backend Skip TLS verification
CMP Verified tls_ca_bundle: path of a CA file readable by the backend Untick TLS verify

Do not confuse the CA that signed the server certificate of the CA endpoint with the issuing CA that signs your certificates: they are often different.

Troubleshooting

Symptom Cause and fix
"Vault must target a single CA: 'issuer_ref' is required" Set the issuer reference (AppRole credential field, or issuer_ref in the connector configuration)
Vault: 403 on every call The policy does not cover the mount or the role; check the paths above
Vault: name refused at signing The role's allowed_domains / allow_subdomains do not cover the requested name
AD CS: "Connexion WinRM echouee (transport ou authentification)" Port 5986 closed, no HTTPS listener, CredSSP not enabled on the server, wrong credentials, or listener certificate not trusted
AD CS: certificate issued in the name of the service account Template set to Build from Active Directory information; switch to Supply in the request
AD CS: request stays pending The template requires CA manager approval
AD CS: ACCESS_DENIED (0x5) on revocation or absorption Issue and Manage Certificates missing on the CA
EJBCA: 403 on every call The client certificate's role lacks /administrator, or the certificate is not bound to a role
EJBCA: empty template list The v2 profile service is disabled; use Create Manually
EJBCA: revocation fails The Issuer DN is not the exact DN of the issuing CA
Status Test failed, cause network The backend cannot reach the CA: firewall, DNS, or a proxy that should be bypassed (Outbound proxy = direct)