This CA handler enrolls certificates through the Microsoft ICertPassage Remote Protocol (MS-ICPR) RPC interface used by Active Directory Certificate Services. Request/response structures align with concepts from MS-WCCE, but transport is MS-ICPR (not DCOM). The handler incorporates code from Certipy.
Compatibility: The previous module name
acme2certifier.cahandlers.mswcce_ca_handlerremains available as a deprecated alias. Prefermsicpr_ca_handler. See also the legacy doc page mswcce.md.
Be aware of the following limitations when using this handler:
- The CA certificate chain is taken from the MS-ICPR enrollment response (
pctbCert/ CMS) when the CA returns it. Configureca_bundleonly as a local PEM fallback if the response has no usable chain.ca_bundledoes not authenticate the CA endpoint. Enrollment uses SMB/DCE-RPC with RPC packet privacy, not HTTPS/TLS verification. - HTTP(S)
proxy_server_listsettings are ignored. MS-ICPR does not use HTTP(S) and cannot be routed through an HTTP proxy. - Revocation operations are not yet supported.
- Pending (manager-approved) enrollments return the CA
request_idas the ACME poll identifier, butpoll()is not implemented yet, so pending requests cannot be completed via ACME.
- Active Directory Certificate Services (AD-CS) must be enabled and properly configured.
- The CA handler communicates with the CA over SMB/named pipes and DCE-RPC (MS-ICPR). The CA must be reachable on TCP port 445. When named-pipe binding is unavailable, the handler falls back to the RPC Endpoint Mapper (TCP 135) and a dynamic high TCP port returned by the CA.
- (Optional): If installing from RPM or DEB and planning to use Kerberos authentication, ensure you have an updated Impacket module (version 0.11 or higher), as older versions have issues handling UTF-8 encoded passwords. You can find updated packages in the A2C GitHub repository.
- You need a set of credentials with sufficient permissions to access the service and enrollment templates.
When using the MS-ICPR handler, acme2certifier communicates with the Microsoft CA through the MS-ICPR RPC interface. This interface does not honor static DCOM endpoint configuration for "CertSrv Request" and will resolve dynamic RPC ports via the CA’s RPC Endpoint Mapper when TCP transport is used. As a result, RPC high ports (or an explicitly allowed dynamic range) must be open between acme2certifier and the CA when named-pipe enrollment is not sufficient—even if the CA has been configured to use a fixed DCOM port for certificate requests.
- Install the Impacket module.
Some malware scanners, such as Microsoft Defender, classify Impacket as a hacking tool (see Fortra Impacket Issue #1762 or Fortra Impacket Issue #1271). These alerts are triggered mainly by example scripts included in the package, not the library itself.
To avoid issues with your security team, consider installing a stripped-down version of Impacket without flagged scripts. Pre-packaged versions are available for RHEL 8 and RHEL 9 in the SBOM repository.
If installing from pip or source, follow these steps:
- Download the Impacket package:
pip3 download impacket --no-deps- Unpack the archive:
tar xvfz impacket-0.11.0.tar.gz- Remove all files and subdirectories in the
examplesdirectory:
rm -rf impacket-0.11.0/examples/*- Install the package:
pip install .
# or: pip install acme2certifierModify the server configuration (acme_srv/acme_srv.cfg) and add the following parameters:
[CAhandler]
handler_module: acme2certifier.cahandlers.msicpr_ca_handler
host: <hostname>
user: <username>
password: <password>
krb5_auth_backend: impacket
krb5_principal: <principal@REALM>
krb5_keytab: </path/to/keytab>
krb5_cache: </path/to/ccache>
krb5_config: </path/to/krb5.conf>
krb5_kinit_path: </path/to/kinit>
target_domain: <domain_name>
domain_controller: <IP_of_domain_controller>
ca_name: <ca_name>
ca_bundle: <filename>
template: <template_name>
timeout: 5
use_kerberos: False
allowed_templates: ["WebServer", "WebServerModified"]
allowed_domainlist: ["example.com", "*.example2.com"]- host – The hostname of the system providing the enrollment service. Multiple hosts can be specified as
server1, server2, server3; a random host will be selected. - host_variable (optional) – Environment variable containing the host address (overridden if
hostis set inacme_srv.cfg). - ca_name – Certificate authority name. Multiple CA names can be specified as
ca1, ca2, ca3; a random entry will be chosen. - user – Username for accessing the service.
- user_variable (optional) – Environment variable containing the username (overridden if
useris set inacme_srv.cfg). - password – Password for authentication.
- password_variable (optional) – Environment variable containing the password (overridden if
passwordis set inacme_srv.cfg). - krb5_auth_backend (optional) – Kerberos backend selection. Supported values are
impacketandpython. Default isimpacket. Ifuse_kerberos=Trueand bothkrb5_principalandkrb5_keytabare configured, the handler auto-selectspythonwhenkrb5_auth_backendis not explicitly set. Forpython, the handler loads a TGT from the ccache and passes it to impacket explicitly (no process-wideKRB5CCNAMEmutation during enrollment). - krb5_principal (optional, required for keytab mode) – Kerberos principal, for example
svc-a2c-enroll@EXAMPLE.COM. - krb5_principal_variable (optional) – Environment variable containing the Kerberos principal (overridden if
krb5_principalis set inacme_srv.cfg). - krb5_keytab (optional, required for keytab mode) – Path to the Kerberos keytab file used by the service account.
- krb5_keytab_variable (optional) – Environment variable containing the keytab path (overridden if
krb5_keytabis set inacme_srv.cfg). - krb5_cache (optional) – Path to the Kerberos credential cache (ccache). Required when using
krb5_auth_backend=impackettogether with keytab mode. Forkrb5_auth_backend=python, a temporary ccache is created automatically if not configured. If you set a shared path used by multiple worker processes or threads, concurrentkinit/ticket refresh can race on the same file; prefer a per-process temporary cache, or a dedicated cache with a single writer and clear operational ownership. - krb5_cache_variable (optional) – Environment variable containing the ccache path (overridden if
krb5_cacheis set inacme_srv.cfg). - krb5_config (optional) – Path to a custom
krb5.conffile. Used by the kinit fallback path. - krb5_config_variable (optional) – Environment variable containing the
krb5.confpath (overridden ifkrb5_configis set inacme_srv.cfg). - krb5_kinit_path (optional) – Full path to the
kinitbinary used by the kinit fallback path. Defaults tokinitresolved fromPATH. If set, the value must be an absolute path whose basename is exactlykinit(for example/usr/bin/kinit). Symlink targets such as Debian/Ubuntukinit.mit/kinit.heimdalare accepted after resolution. Other values are rejected and the kinit fallback fails. - krb5_kinit_path_variable (optional) – Environment variable containing the
kinitbinary path (overridden ifkrb5_kinit_pathis set inacme_srv.cfg). - target_domain (optional) – Active Directory domain name.
- domain_controller (optional) – Domain controller endpoint. You can provide either an IP address or an FQDN. If an FQDN is configured, acme2certifier resolves it via DNS and uses the first returned IP address.
- dns_server (optional) – IP address of the DNS server.
- ca_bundle (optional) – Local CA certificate chain in PEM format used as a packaging fallback when the enrollment response does not include a CMS chain. Appended to the issued certificate only; it does not verify the AD CS enrollment endpoint.
- template – Certificate template used for enrollment.
- allowed_templates (optional) – JSON list of ADCS templates permitted for enrollment (including templates selected via ACME profiles,
header_info, or EAB). An empty or unset list allows any template and logs a warning (backwards compatible). When non-empty, enrollment is rejected if the selected template is not listed. EAB per-account template restrictions still apply on top of this global ceiling. MS-ICPR has no Web Enrollment template discovery API; CA-side membership checks are mscertsrv-only. - timeout (optional) – Enrollment timeout in seconds (default:
5). - use_kerberos – Use Kerberos for authentication. If
False, authentication is done via NTLM. Due to Microsoft's October 2023 announcement, Kerberos is recommended, but NTLM remains the default for backward compatibility. Startup logs a warning when Kerberos is disabled. - allowed_domainlist (optional) – List of allowed domains for enrollment (JSON format).
- enrollment_config_log (optional) – Log enrollment parameters (default:
False). This handler omitspassword, Kerberos credential locations (krb5_keytab,krb5_cache,krb5_config,krb5_kinit_path), and runtime TGT objects from that dump. - enrollment_config_log_skip_list (optional) – List of enrollment parameters to exclude from logs (JSON format).
Keytab-based Kerberos authentication allows the CA handler to authenticate without storing a reusable plaintext password in acme_srv.cfg. This reduces credential exposure risk in configuration management systems, log archives, and backup snapshots as
- Passwords no longer need to be kept in clear text in the CA handler configuration.
- Service-account credentials can be rotated and scoped following AD operational controls.
- Keytab files can be protected with strict filesystem ACLs and isolated runtime identities.
The usual approach is to create or reuse a dedicated service account and generate a keytab with ktpass.
- Open an elevated command prompt or PowerShell on a domain controller.
- Generate the keytab (example):
ktpass /princ svc-a2c-enroll@EXAMPLE.COM /mapuser EXAMPLE\svc-a2c-enroll /crypto AES256-SHA1 /ptype KRB5_NT_PRINCIPAL /out C:\Temp\svc-a2c-enroll.keytab /pass *- Securely copy the keytab to the acme2certifier host (for example
/etc/acme2certifier/svc-a2c-enroll.keytab). - Restrict file permissions so only the service user can read it.
Notes:
ktpasscan reset or affect account password/key material depending on options and AD state. Validate your AD policy and coordinate with AD administrators before running it in production.- Prefer modern encryption types (for example AES256) and avoid legacy ciphers.
[CAhandler]
handler_module: acme2certifier.cahandlers.msicpr_ca_handler
host: <ca-hostname>
target_domain: EXAMPLE.COM
domain_controller: <dc-ip-or-name>
ca_name: <ca-name>
ca_bundle: <ca-bundle-path>
template: <template-name>
use_kerberos: True
krb5_principal: svc-a2c-enroll@EXAMPLE.COM
krb5_keytab: /etc/acme2certifier/svc-a2c-enroll.keytab
# Optional
krb5_auth_backend: python
krb5_cache: /var/www/acme2certifier/volume/krb5cc_a2c
krb5_config: /etc/krb5.conf
krb5_kinit_path: /usr/bin/kinitBefore starting production enrollment, verify Kerberos ticket acquisition from the acme2certifier host:
kdestroy || true
export KRB5CCNAME=/tmp/krb5cc_a2c_test
kinit -k -t /etc/acme2certifier/svc-a2c-enroll.keytab svc-a2c-enroll@EXAMPLE.COM
klist
klist -k /etc/acme2certifier/svc-a2c-enroll.keytabExpected result:
kinitexits successfully.klistshows a valid TGT for the service principal.- Keytab entries are visible with
klist -k.
acme2certifier supports the Automated Certificate Management Environment (ACME) Profiles Extension draft, allowing an acme-client to specify a template parameter to be submitted to the CA server.
The list of supported profiles must be configured in acme_srv.cfg
[Order]
profiles: {"template1": "http://foo.bar/template1", "template2": "http://foo.bar/template2", "template3": "http://foo.bar/template3"}
Once enabled, a client can specify the template to be used as part of an order request. Below an example for lego:
docker run -i -v $PWD/lego:/.lego/ --rm --name lego goacme/lego run --tls-skip-verify -s https://<acme-srv> -a --email "lego@example.com" -d <fqdn> --http --profile template2Further, this handler uses the header_info_list feature, allowing an ACME client to specify a template name for certificate enrollment. To enable this feature, update acme_srv.cfg:
[Order]
header_info_list: ["HTTP_USER_AGENT"]- acme.sh:
docker exec -i acme-sh acme.sh --server http://<acme-srv> --issue -d <fqdn> --standalone --useragent template=foo --debug 3 --output-insecure- lego:
docker run -i -v $PWD/lego:/.lego/ --rm --name lego goacme/lego run --tls-skip-verify -s https://<acme-srv> -a --email "lego@example.com" --user-agent template=foo -d <fqdn> --httpThis handler supports EAB profiling, which allows individual enrollment configurations per ACME account and restricts CN/SANs in the CSR. To enable this feature, update acme_srv.cfg:
[EABhandler]
eab_handler_module: acme2certifier.eabhandlers.kid_profile_handler
key_file: <profile_file>
eab_profiling: True
[CAhandler]
...{
"keyid_00": {
"hmac": "example_hmac_value",
"cahandler": {
"template": ["WebServerModified", "WebServer"],
"allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"],
"unknown_key": "unknown_value"
}
},
"keyid_01": {
"hmac": "YW5vdXRoZXJfdmVyeV9sb25nX2htYWNfZm9yX2tleWlkXzAxX3doaWNoIHdpbGxfYmUgdXNlZF9kdXJpbmcgcmVncmVzc2lvbg",
"cahandler": {
"template": "WebServerModified",
"allowed_domainlist": ["www.example.com", "www.example.org", "*.acme"],
"unknown_key": "unknown_value"
}
},
"keyid_02": {
"hmac": "dGhpc19pc19hX3ZlcnlfbG9uZ19obWFjX3RvX21ha2Vfc3VyZV90aGF0X2l0c19tb3JlX3RoYW5fMjU2X2JpdHM",
"cahandler": {
"allowed_domainlist": ["www.example.com", "www.example.org"]
}
},
"keyid_03": {
"hmac": "YW5kX2ZpbmFsbHlfdGhlX2xhc3RfaG1hY19rZXlfd2hpY2hfaXNfbG9uZ2VyX3RoYW5fMjU2X2JpdHNfYW5kX3Nob3VsZF93b3Jr"
}
}This setup ensures that individual accounts can have specific enrollment configurations and domain restrictions.