// insights

How to build an ADCS issuing CA on a Luna HSM HA group

Contents

This is the build companion to the issuing CA concept chapter, which covered why the issuing tier carries the availability burden and how a CA key ends up on exactly one HSM. This post is the walkthrough: an ADCS enterprise issuing CA on Windows Server, with its key generated into a Luna HSM HA group’s virtual slot so it exists on every member, verified per member, and proven with a live member-failure drill. The sequencing matters more than any individual command — the single most important property of this build is that the CA server never sees a physical slot, so the key cannot be generated in the wrong place.

What you need before you start

This walkthrough assumes the platform work is already done and evidenced:

  • An HA group that exists and synchronises. Two or more Luna Network HSM partitions, initialised in the same cloning domain, joined into a group and drilled — the full procedure is in how to build and failover-test a Luna HSM HA group. If hagroup listgroups does not show every member alive and Needs sync: no, stop and fix that first.
  • Auto-recovery configured on the CA server’s client. HA settings are per-client; the CA server needs its own retry count, interval and recovery mode — settings made on the HSM team’s admin box do not transfer.
  • The Luna HSM Client installed on the CA server, NTLS (or STC) registered to every member partition, and connectivity confirmed with lunacm.
  • A root CA able to sign the subordinate certificate. For an offline root on its own Luna, that ceremony is its own chapter.
  • The CA service account decision made. The SafeNet KSP must be registered for both the operating context and NT AUTHORITY\SYSTEM — the mechanics and the gotcha are in registering the Luna KSP for an ADCS CA.

For the wider platform context — appliances, partitions, client architecture — see our Luna HSM design and operational guide.

Step 1: point the CA server’s Luna client at the virtual slot only

On the CA server, open LunaCM and confirm the client sees the HA group and its members:

C:\Program Files\SafeNet\LunaClient> lunacm.exe

lunacm:> hagroup listgroups

   HA Group Label:  ISSUING-CA-HA
   HA Group Number: 1154438865288
   ...
   Needs sync:      no

At this point the client is presenting the physical member slots and the virtual slot, and the physical slots usually carry the lower slot numbers. This is exactly the state in which CA keys get generated against one appliance. Close it off before going anywhere near ADCS by enabling HA-only mode, which hides the physical slots from applications on this client:

lunacm:> hagroup haonly -enable

        "HA Only" has been enabled.

lunacm:> slot list

After enabling, slot list should show the HA virtual slot as the only application-visible slot. Thales’s ADCS integration guidance is explicit that HA-only must be enabled for failover to work — with it off, calls made against a physical slot have no failover logic at all. Note that this is a per-client setting on the CA server; it does not affect the admin workstation you will later use for per-member verification.

Step 2: register the SafeNet KSP against the virtual slot

Run KspConfig.exe from the Luna client’s KSP directory. Register the security library (cryptoki.dll from the Luna client installation), then register the HSM slot twice: once for the domain account context the CA configuration will run under, and once for NT AUTHORITY\SYSTEM, which is the context certsvc actually uses at runtime. Because HA-only is already enabled, the only slot on offer is the virtual slot — the registration cannot silently bind to member A’s partition, which is the trap the concept chapter dissects.

Skipping the SYSTEM registration is the classic failure: the wizard completes, then the CA service cannot open its key. The full procedure, including where registrations land in the registry and how to verify them, is in the KSP registration how-to.

Step 3: install ADCS with the key generated into the virtual slot

Install the role and run the configuration, selecting the SafeNet provider so the new CA key is generated through the KSP — which now can only mean the virtual slot. The HA client generates the key on the primary member and clones it to every other member as part of creation. Via PowerShell:

Install-WindowsFeature ADCS-Cert-Authority -IncludeManagementTools

Install-AdcsCertificationAuthority `
    -CAType EnterpriseSubordinateCA `
    -CACommonName "ORG-ISSUING-CA-01" `
    -CryptoProviderName "RSA#SafeNet Key Storage Provider" `
    -KeyLength 4096 `
    -HashAlgorithmName SHA256

If you prefer the Server Manager wizard, the equivalent choice is selecting an algorithm under SafeNet Key Storage Provider in the cryptographic provider dropdown when creating the new private key. Two practical notes:

  • Leave “allow administrator interaction when the private key is accessed” unticked for an issuing CA — it must sign unattended.
  • On Luna FIPS firmware 7.7.2 and later, Thales documents that certificates enrolled through the SafeNet KSP cannot perform encrypt/decrypt operations under FIPS restrictions — a consideration for what you put on this CA, not for the CA signing key itself.

Complete the subordinate certificate request against your root, install the CA certificate, and start the service. Then confirm the CA can open its key through the KSP:

sc query certsvc

certutil -verifykeys

  ... CA keys have been successfully verified

Step 4: verify the key exists on every member

certutil -verifykeys proves the CA can use its key through the virtual slot. It says nothing about how many members hold that key — a key on one member behind a healthy virtual slot passes this check all day. Per-member verification needs a client that can see the physical slots, so run it from an admin workstation registered to each member partition without HA-only (or temporarily disable HA-only there in a change window — never on the CA server). Against each member’s slot, list private keys with cmu:

cmu list -slot=0 -display=class,keyType,label -class=private

  class=privateKey    keyType=RSA    label=ORG-ISSUING-CA-01

cmu list -slot=1 -display=class,keyType,label -class=private

  class=privateKey    keyType=RSA    label=ORG-ISSUING-CA-01

The pass condition is exact: the CA key’s label appears in every member partition. A label present on one member and absent on another is the single-member trap, caught before it becomes an incident. Record the output — this is precisely the kind of availability evidence an IRAP assessment expects for ISM-aligned certificate services, and you will repeat this check after every member replacement or group change.

Step 5: run the member-failure drill against the CA

A generic HA drill proves the group fails over. This drill proves the CA does. Schedule a window, and generate continuous issuance load from a domain-joined test machine using a low-impact test template:

# Run on a test client during the drill window
while ($true) {
    Get-Certificate -Template "HADrillComputer" `
        -CertStoreLocation Cert:\LocalMachine\My
    Start-Sleep -Seconds 2
}

With load running, fail a member — pull its network cable or power it down cleanly, per your change record. What you should observe, in order:

  • A pause, not an outage. Requests stall for roughly the client’s detection window — about 20 seconds on default timeouts, tunable and version-dependent — then complete against the surviving member.
  • Possibly a small number of hard failures. An operation mid-flight on the dead member can surface an error for that individual request. Confirm the enrolment layer retries it — autoenrolment will on its next pulse; your loop simply issues again.
  • No CA service failure. certsvc stays running throughout. If the CA stops issuing entirely when one specific member is pulled, you have just found a single-member key the cheap way — go back to step 4.
  • Recovery without a human. Restore the member and watch auto-recovery return it to alive and resynchronise. If it stays failed, the CA server’s client-side recovery settings are wrong — fix them now, not during an incident.

Keep timestamps, the observed pause, any failed requests and their retries. That artefact converts “we have HA” into evidence.

Step 6: wire the checks into monitoring

Everything above decays silently unless something watches it. The minimum standing set for an issuing CA on Luna HA:

  • Group state on a schedule: scrape hagroup listgroups from the CA server’s client; alert on any member not alive, persistent Needs sync: yes, or auto-recovery showing disabled.
  • The client-side HA log shipped to your SIEM — it is the only record of failover events you get, and it is off by default.
  • Per-member key presence: the step 4 cmu list check, run periodically from the admin workstation and after every group change.
  • CA-level health: certutil -verifykeys on a schedule, plus alerting on CRL publication well inside its expiry window — CRL expiry is how an HSM problem becomes an estate-wide authentication outage.
  • An annual member-failure drill, run against the CA under issuance load, with the evidence filed.

Where this leaves you

You now have an issuing CA whose key provably exists on every HSM in the group, with a drill on record showing it rides through member failure. The next demand on that discipline comes from outside the PKI team: the CA/Browser Forum’s hardware mandate pushed every publicly-trusted code signing key onto HSMs, and those keys arrive with CI pipelines attached and none of this operational rigour — that is the next chapter. The full series lives at The Luna Field Guide.

// cryptographic hsm solutions
Running hardware you can’t afford to get wrong?

Securitribe designs, deploys and operates Thales Luna HSM estates — partitions, HA, key ceremonies and DR that stand up to audit.

Explore Cryptographic HSM SolutionsBook a strategy call →
// more insights

Keep reading