PKI Troubleshooting

Error 0x80094800: Template Not Supported

Symptom

Certificate not issued (Denied) Denied by Policy Module  0x80094800
The requested certificate template is not supported by the Active Directory Certificate Services policy: Linux-Workstation-Auth.
The requested certificate template is not supported by this CA. 0x80094800 (-2146875392 CERTSRV_E_UNSUPPORTED_CERT_TYPE)

This error occurs when submitting a CSR with certreq:

certreq -submit -config "{ad-server}\HOME-ROOT-CA" \
  -attrib "CertificateTemplate:Linux-Workstation-Auth" \
  "C:\Certs\hostname-eaptls.csr" "C:\Certs\hostname-eaptls.cer"

Root Causes

This error has four distinct root causes that must ALL be fixed:

  1. Missing Enroll Permission

  2. Missing ENROLLEE_SUPPLIES_SUBJECT Flag

  3. Incompatible Schema Version

  4. Invalid Validity Period Encoding

Each must be addressed for the template to work.


Fix 1: Add Enroll Permission for Authenticated Users

Diagnosis

Check if Authenticated Users has Enroll permission:

ssh home-dc01 'dsacls "CN=Linux-Workstation-Auth,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=inside,DC=domusdigitalis,DC=dev" | findstr /i "enroll"'

If output shows only READ permissions and no "Enroll" extended right, this fix is needed.

Solution

Add Certificate-Enrollment extended right for Authenticated Users:

# Create PowerShell script
cat > /tmp/fix-template-permissions.ps1 << 'EOF'
Import-Module ActiveDirectory

$ConfigNC = (Get-ADRootDSE).configurationNamingContext
$TemplateDN = "CN=Linux-Workstation-Auth,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigNC"

Write-Host "Getting ACL for template..."
$ACL = Get-Acl -Path "AD:$TemplateDN"

Write-Host "Creating identity for Authenticated Users..."
$Identity = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-11")

Write-Host "Creating Enroll GUID..."
$EnrollGUID = New-Object Guid "0e10c968-78fb-11d2-90d4-00c04f79dc55"

Write-Host "Creating access rule..."
$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
    $Identity, `
    ([System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight), `
    ([System.Security.AccessControl.AccessControlType]::Allow), `
    $EnrollGUID

Write-Host "Adding access rule..."
$ACL.AddAccessRule($AccessRule)

Write-Host "Applying ACL..."
Set-Acl -Path "AD:$TemplateDN" -AclObject $ACL

Write-Host "SUCCESS: Certificate-Enrollment permission granted to Authenticated Users"
EOF

# Transfer and execute
scp /tmp/fix-template-permissions.ps1 home-dc01:C:/Temp/
ssh home-dc01 "powershell.exe -ExecutionPolicy Bypass -File C:\Temp\fix-template-permissions.ps1"

Verification

ssh home-dc01 'dsacls "CN=Linux-Workstation-Auth,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=inside,DC=domusdigitalis,DC=dev" | findstr /i "enroll"'

Expected output should show "Enroll" permission.


Fix 2: Add ENROLLEE_SUPPLIES_SUBJECT Flag

Diagnosis

Check template flags:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''} -Properties flags | Select-Object Name,flags"'

Compare with working WebServer template:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Web Server'''} -Properties flags | Select-Object Name,flags"'

If Linux-Workstation-Auth flags = 131680 and WebServer flags = 66113, the ENROLLEE_SUPPLIES_SUBJECT flag is missing.

Solution

Add ENROLLEE_SUPPLIES_SUBJECT flag (bit 0):

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; $template = Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''}; Set-ADObject -Identity \$template -Replace @{flags = 131681}"'

This sets flags from 131680 to 131681 (adding bit 0).

Verification

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''} -Properties flags | Select-Object Name,flags"'

Expected: flags : 131681


Fix 3: Downgrade Schema Version

Diagnosis

Check template schema version:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''} -Properties msPKI-Template-Schema-Version | Select-Object Name,msPKI-Template-Schema-Version"'

If schema version = 2, but CA only supports version 1 templates, this fix is needed.

Compare with WebServer:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Web Server'''} -Properties msPKI-Template-Schema-Version | Select-Object Name,msPKI-Template-Schema-Version"'

WebServer should show version 1.

Solution

Downgrade schema version from 2 to 1:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; $template = Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''}; Set-ADObject -Identity \$template -Replace @{'''msPKI-Template-Schema-Version''' = 1}"'

Verification

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''} -Properties msPKI-Template-Schema-Version | Select-Object Name,msPKI-Template-Schema-Version"'

Expected: msPKI-Template-Schema-Version : 1


Fix 4: Correct Validity Period Encoding

Symptom

After fixing the above three issues, error changes to:

Certificate not issued (Denied) Denied by Policy Module The specified time is invalid. 0x8007076d (WIN32: 1901 ERROR_INVALID_TIME)

This indicates the validity period encoding is incorrect.

Diagnosis

Check validity periods:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Linux-Workstation-Auth'''} -Properties pKIExpirationPeriod | Select-Object Name,pKIExpirationPeriod"'

Compare with working WebServer template:

ssh home-dc01 'powershell.exe -Command "$ConfigNC = (Get-ADRootDSE).configurationNamingContext; Get-ADObject -SearchBase \"CN=Certificate Templates,CN=Public Key Services,CN=Services,\$ConfigNC\" -Filter {displayName -eq '''Web Server'''} -Properties pKIExpirationPeriod | Select-Object Name,pKIExpirationPeriod"'

If byte arrays differ, the encoding is incorrect.

Solution

Copy the working validity period from WebServer template:

# Create PowerShell script
cat > /tmp/fix-validity-period.ps1 << 'EOF'
$ConfigNC = (Get-ADRootDSE).configurationNamingContext
$webserver = Get-ADObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigNC" -Filter {displayName -eq 'Web Server'} -Properties pKIExpirationPeriod
$template = Get-ADObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigNC" -Filter {displayName -eq 'Linux-Workstation-Auth'}
Set-ADObject -Identity $template -Replace @{'pKIExpirationPeriod' = $webserver.pKIExpirationPeriod}
Write-Host "Validity period updated to match WebServer template"
Restart-Service certsvc
Write-Host "CA service restarted"
EOF

# Transfer and execute
scp /tmp/fix-validity-period.ps1 home-dc01:C:/Temp/
ssh home-dc01 "powershell.exe -ExecutionPolicy Bypass -File C:\Temp\fix-validity-period.ps1"
The CA service (certsvc) must be restarted for validity period changes to take effect.

Apply All Fixes

Once all four fixes are applied, restart the CA service and test:

Restart CA Service

ssh home-dc01 "net stop certsvc && net start certsvc"

Or via PowerShell:

ssh home-dc01 "powershell.exe -Command 'Restart-Service certsvc'"

Test Certificate Enrollment

HOSTNAME=$(cat /etc/hostname)

ssh home-dc01 <<EOF
del C:\\Certs\\${HOSTNAME}-eaptls.rsp
certreq -submit -config "{ad-server}\\HOME-ROOT-CA" -attrib "CertificateTemplate:Linux-Workstation-Auth" "C:\\Certs\\${HOSTNAME}-eaptls.csr" "C:\\Certs\\${HOSTNAME}-eaptls.cer"
EOF

Expected output:

RequestId: 77
RequestId: "77"
Certificate retrieved(Issued) Issued

Complete Fix Script

For convenience, here’s a complete script that applies all fixes:

#!/bin/bash
# fix-linux-workstation-auth-template.sh
# Fixes all four issues with Linux-Workstation-Auth template

set -euo pipefail

echo "=== Fixing Linux-Workstation-Auth Template ==="

# Fix 1: Add Enroll Permission
cat > /tmp/fix-all-template-issues.ps1 << 'EOF'
Import-Module ActiveDirectory

$ConfigNC = (Get-ADRootDSE).configurationNamingContext
$TemplateDN = "CN=Linux-Workstation-Auth,CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigNC"

# Fix 1: Add Enroll Permission
Write-Host "Fix 1: Adding Enroll permission..."
$ACL = Get-Acl -Path "AD:$TemplateDN"
$Identity = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-11")
$EnrollGUID = New-Object Guid "0e10c968-78fb-11d2-90d4-00c04f79dc55"
$AccessRule = New-Object System.DirectoryServices.ActiveDirectoryAccessRule `
    $Identity, `
    ([System.DirectoryServices.ActiveDirectoryRights]::ExtendedRight), `
    ([System.Security.AccessControl.AccessControlType]::Allow), `
    $EnrollGUID
$ACL.AddAccessRule($AccessRule)
Set-Acl -Path "AD:$TemplateDN" -AclObject $ACL
Write-Host "  ✓ Enroll permission added"

# Fix 2: Add ENROLLEE_SUPPLIES_SUBJECT flag
Write-Host "Fix 2: Adding ENROLLEE_SUPPLIES_SUBJECT flag..."
$template = Get-ADObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigNC" -Filter {displayName -eq 'Linux-Workstation-Auth'}
Set-ADObject -Identity $template -Replace @{flags = 131681}
Write-Host "  ✓ Flag added"

# Fix 3: Downgrade schema version
Write-Host "Fix 3: Setting schema version to 1..."
Set-ADObject -Identity $template -Replace @{'msPKI-Template-Schema-Version' = 1}
Write-Host "  ✓ Schema version set to 1"

# Fix 4: Copy validity period from WebServer
Write-Host "Fix 4: Fixing validity period..."
$webserver = Get-ADObject -SearchBase "CN=Certificate Templates,CN=Public Key Services,CN=Services,$ConfigNC" -Filter {displayName -eq 'Web Server'} -Properties pKIExpirationPeriod
Set-ADObject -Identity $template -Replace @{'pKIExpirationPeriod' = $webserver.pKIExpirationPeriod}
Write-Host "  ✓ Validity period corrected"

# Restart CA service
Write-Host "Restarting Certificate Authority service..."
Restart-Service certsvc
Write-Host "  ✓ CA service restarted"

Write-Host ""
Write-Host "SUCCESS: All fixes applied to Linux-Workstation-Auth template"
EOF

# Transfer and execute
echo "Transferring script to home-dc01..."
scp /tmp/fix-all-template-issues.ps1 home-dc01:C:/Temp/

echo "Executing fixes on home-dc01..."
ssh home-dc01 "powershell.exe -ExecutionPolicy Bypass -File C:\Temp\fix-all-template-issues.ps1"

echo ""
echo "=== Template Fixed - Ready for Certificate Enrollment ==="

Save as fix-linux-workstation-auth-template.sh, make executable, and run:

chmod +x fix-linux-workstation-auth-template.sh
./fix-linux-workstation-auth-template.sh

Prevention

When creating new certificate templates for Linux workstation authentication:

  1. Always set Authenticated Users permissions:

    • Read

    • Enroll

  2. Use Schema Version 1 (Windows 2000 compatibility)

  3. Set flags to include ENROLLEE_SUPPLIES_SUBJECT

  4. Use working templates as base:

    • Duplicate "Web Server" template instead of "Workstation Authentication"

    • Web Server template has correct encoding and compatibility

  5. Test immediately after creation:

    • Submit test CSR

    • Verify no 0x80094800 errors


Error: Certificate/Key Mismatch - EAP-TLS Fails

Symptom

802.1X authentication fails immediately during TLS handshake:

Jan 27 20:55:24 modestus-razer wpa_supplicant[3337]: OpenSSL: tls_read_pkcs12 - Failed to use PKCS#12 file error:05800074:x509 certificate routines::key values mismatch
Jan 27 20:55:24 modestus-razer wpa_supplicant[3337]: TLS: Failed to load private key '{key-dir}/hostname-eaptls.key'
Jan 27 20:55:24 modestus-razer wpa_supplicant[3337]: EAP-TLS: Failed to initialize SSL.
Jan 27 20:55:24 modestus-razer wpa_supplicant[3337]: enp130s0: CTRL-REQ-PASSPHRASE-0:Private key passphrase needed for SSID

NetworkManager shows:

Error: Connection activation failed: Secrets were required, but not provided

The supplicant state reaches "associated" but never completes:

Jan 27 20:55:22 modestus-razer NetworkManager[3287]: <info>  [1769576122.1423] device (enp130s0): supplicant interface state: disconnected -> associated
Jan 27 20:55:47 modestus-razer NetworkManager[3287]: <warn>  [1769576147.0386] device (enp130s0): Activation: (ethernet) association took too long.

Root Cause

The certificate and private key do not match.

This happens when: - You generated multiple CSRs and got them confused - You installed the wrong key file from /tmp - You regenerated the CSR without replacing the certificate

Diagnosis

Verify certificate and key match by comparing their modulus hashes:

# Check certificate modulus
openssl x509 -noout -modulus -in {cert-dir}/hostname-eaptls.pem | openssl md5

# Check private key modulus (needs sudo)
sudo openssl rsa -noout -modulus -in {key-dir}/hostname-eaptls.key | openssl md5

Hashes MUST match exactly. Example:

# Certificate
MD5(stdin)= b82b8dd7c0f1c3aafc4e31c046b64edf

# Private key
MD5(stdin)= b82b8dd7c0f1c3aafc4e31c046b64edf  ✓ Match!

If hashes differ, the cert and key don’t belong together:

# Certificate
MD5(stdin)= b82b8dd7c0f1c3aafc4e31c046b64edf

# Private key
MD5(stdin)= b66262b6f8bc42a15141fb3386b6e6ee  ✗ MISMATCH!

Solution

Find the correct matching key file and install it:

# Check all potential key files in /tmp
for keyfile in /tmp/*.key; do
    echo "=== Checking $keyfile ==="
    openssl rsa -noout -modulus -in "$keyfile" 2>/dev/null | openssl md5
done

# Compare to certificate hash
openssl x509 -noout -modulus -in {cert-dir}/hostname-eaptls.pem | openssl md5

# Install the matching key
sudo cp /tmp/hostname-eaptls.key {key-dir}/hostname-eaptls.key
sudo chmod 600 {key-dir}/hostname-eaptls.key

# Verify they match now
openssl x509 -noout -modulus -in {cert-dir}/hostname-eaptls.pem | openssl md5
sudo openssl rsa -noout -modulus -in {key-dir}/hostname-eaptls.key | openssl md5

Test Fix

Reconnect and verify:

# Reconnect
sudo nmcli connection up "Wired-802.1X"

# Check success
nmcli device show enp130s0 | grep -E "STATE|IP4.ADDRESS"

# Should show:
# GENERAL.STATE:    100 (connected)
# IP4.ADDRESS[1]:   10.50.40.xxx/24

Prevention

To avoid this issue:

  1. Keep key and CSR together - Generate both in the same directory with matching names: bash openssl req -new -newkey rsa:2048 -nodes \ -keyout /tmp/$(hostname)-eaptls.key \ -out /tmp/$(hostname)-eaptls.csr \ -subj "/O=Domus Digitalis/OU=Endpoints/CN=$(hostname).inside.domusdigitalis.dev"

  2. Don’t regenerate CSR - If you need a new certificate, regenerate BOTH the key and CSR together

  3. Install immediately - After getting the signed certificate, install both files right away: bash sudo cp /tmp/$(hostname)-eaptls.pem /etc/ssl/certs/ sudo cp /tmp/$(hostname)-eaptls.key /etc/ssl/private/ sudo chmod 644 /etc/ssl/certs/$(hostname)-eaptls.pem sudo chmod 600 /etc/ssl/private/$(hostname)-eaptls.key

  4. Verify match before using - Always verify hash match before configuring 802.1X