PowerShell Certificates

Certificate management for enterprise 802.1X. Machine and user certificate stores, EAP-TEAP validation, AD CS auto-enrollment, WLAN profile certificate extraction, and event log troubleshooting — the commands you need when a workstation fails authentication against ISE/RADIUS.

Certificate Stores You’ll Touch Daily
  • Cert:\LocalMachine\My — Machine certificates (computer identity for 802.1X machine auth)

  • Cert:\CurrentUser\My — User certificates (user identity for 802.1X user auth)

  • Cert:\LocalMachine\Root — Trusted Root CAs (must contain your enterprise root CA)

  • Cert:\LocalMachine\CA — Intermediate CAs (must contain your subordinate CA)

Certificates Quick Reference

Machine Certificates (802.1X Machine Auth)

List all machine certs with expiry countdown
Get-ChildItem Cert:\LocalMachine\My |
    Select-Object Subject, Issuer, NotAfter,
    @{N='DaysLeft';E={($_.NotAfter - (Get-Date)).Days}},
    Thumbprint | Sort-Object NotAfter | Format-Table
Find the machine cert used for 802.1X (Client Authentication EKU)
Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
} | Select-Object Subject, Issuer, NotAfter, Thumbprint
Find certs by issuing CA
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -match "CHLASUBCA" } |
    Format-List Subject, Issuer, NotBefore, NotAfter, Thumbprint, EnhancedKeyUsageList
Identify Intune MDM vs AD CS vs Entra certs
Get-ChildItem Cert:\LocalMachine\My | ForEach-Object {
    $source = switch -Regex ($_.Issuer) {
        'CHLASUBCA'                    { 'AD CS (Enterprise)' }
        'Microsoft Intune'             { 'Intune MDM' }
        'MS-Organization-Access'       { 'Entra ID (AAD)' }
        'Device Management'            { 'Intune Device' }
        default                        { 'Other' }
    }
    [PSCustomObject]@{
        Source  = $source
        Subject = $_.Subject.Substring(0, [Math]::Min(60, $_.Subject.Length))
        Expires = $_.NotAfter
        EKU     = ($_.EnhancedKeyUsageList.FriendlyName -join ', ')
    }
} | Sort-Object Source | Format-Table -AutoSize

Expiry Monitoring

Certs expiring within 30 days (alert threshold)
$threshold = (Get-Date).AddDays(30)
Get-ChildItem Cert:\LocalMachine\My |
    Where-Object { $_.NotAfter -lt $threshold -and $_.NotAfter -gt (Get-Date) } |
    Select-Object Subject, NotAfter, @{N='DaysLeft';E={($_.NotAfter - (Get-Date)).Days}}
Already expired certs (both stores)
'LocalMachine','CurrentUser' | ForEach-Object {
    Get-ChildItem "Cert:\$_\My" | Where-Object { $_.NotAfter -lt (Get-Date) } |
    Select-Object @{N='Store';E={$_}}, Subject, NotAfter
} | Format-Table

Certificate Details

Full cert inspection (by subject match)
Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -like "*chla*" |
    Format-List Subject, Issuer, NotBefore, NotAfter, Thumbprint,
    HasPrivateKey, EnhancedKeyUsageList, SerialNumber
Check certificate template (AD CS)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -match "CHLASUBCA" } | Select-Object -First 1
$templateExt = $cert.Extensions | Where-Object { $_.Oid.Value -eq "1.3.6.1.4.1.311.21.7" }
if ($templateExt) { $templateExt.Format($true) }
Validate certificate chain (is the full chain trusted?)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -match "CHLASUBCA" } | Select-Object -First 1
$chain = New-Object Security.Cryptography.X509Certificates.X509Chain
$valid = $chain.Build($cert)
Write-Host "Chain valid: $valid"
if (-not $valid) { $chain.ChainStatus | ForEach-Object { Write-Host "  Error: $($_.Status)" -ForegroundColor Red } }
Check private key exists (required for EAP-TLS)
Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
} | Select-Object Subject, HasPrivateKey, NotAfter
If HasPrivateKey is $false, 802.1X authentication will fail even if the certificate is otherwise valid. The private key is needed to prove identity during the TLS handshake.

Trust Chain

Trusted Root CAs (is your enterprise root CA here?)
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match "CHLA" } |
    Select-Object Subject, Thumbprint, NotAfter
Intermediate CAs (is your subordinate CA here?)
Get-ChildItem Cert:\LocalMachine\CA | Where-Object { $_.Subject -match "CHLA" } |
    Select-Object Subject, Thumbprint, NotAfter

EAP-TEAP WiFi Profile Inspection

Inspect enterprise WiFi profile (802.1X settings)
netsh wlan show profile name="CHLA_Staff"                        (1)
netsh wlan show profile name="CHLA-Remote"                       (2)
1 Look for: Authentication=WPA2-Enterprise, EAP type=TEAP, 802.1X=Enabled
2 Check Cache user informationNo means re-auth every connect
Extract EAP-TEAP config from exported WLAN profile XML
netsh wlan export profile name="CHLA_Staff" folder="$env:TEMP" key=clear
[xml]$profile = Get-Content "$env:TEMP\Wi-Fi-CHLA_Staff.xml"
$profile.WLANProfile.MSM.security.OneX.EAPConfig.OuterXml
Inspect wired 802.1X profile
netsh lan show profiles interface="Ethernet"
netsh lan export profile folder="$env:TEMP" interface="Ethernet"

Enrollment and Renewal

Trigger machine cert auto-enrollment (AD CS)
certutil -pulse                                                  (1)
gpupdate /force                                                  (2)
1 Triggers certificate auto-enrollment cycle immediately
2 Forces Group Policy refresh which also triggers auto-enrollment
Request cert from specific template
certreq -enroll -q "Workstation Authentication"
Check CA availability
certutil -config - -ping

Event Viewer — 802.1X Troubleshooting

WiFi 802.1X authentication events
Get-WinEvent -LogName "Microsoft-Windows-WLAN-AutoConfig/Operational" -MaxEvents 30 |
    Where-Object { $_.Message -match "802.1X|EAP|TEAP|authentication|fail|success" } |
    Select-Object TimeCreated, Id, LevelDisplayName,
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
Wired 802.1X authentication events
Get-WinEvent -LogName "Microsoft-Windows-Wired-AutoConfig/Operational" -MaxEvents 30 |
    Select-Object TimeCreated, Id, LevelDisplayName,
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
SCHANNEL errors (TLS handshake failures)
Get-WinEvent -LogName "System" -MaxEvents 100 |
    Where-Object { $_.ProviderName -eq "Schannel" } |
    Select-Object TimeCreated, LevelDisplayName,
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
Enable and read CAPI2 logging (certificate chain validation)
# Enable (one-time, Admin):
wevtutil set-log Microsoft-Windows-CAPI2/Operational /enabled:true

# Read:
Get-WinEvent -LogName "Microsoft-Windows-CAPI2/Operational" -MaxEvents 20 |
    Select-Object TimeCreated, @{N='Summary';E={$_.Message.Split("`n")[0]}}
Key 802.1X Event IDs
Log Event ID Meaning

WLAN-AutoConfig

8001

WiFi connected successfully

WLAN-AutoConfig

8002

WiFi connection failed

WLAN-AutoConfig

8003

WiFi disconnected

WLAN-AutoConfig

11001

802.1X authentication started

WLAN-AutoConfig

11002

802.1X authentication succeeded

WLAN-AutoConfig

11004

802.1X authentication failed

WLAN-AutoConfig

11010

WiFi network not available

Wired-AutoConfig

15500

Wired 802.1X started

Wired-AutoConfig

15501

Wired 802.1X succeeded

Wired-AutoConfig

15503

Wired 802.1X failed

Schannel

36887

TLS fatal alert received

Schannel

36888

TLS fatal alert sent

Export / Import

Export cert (no private key — for sharing with ISE admin)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -match "CHLASUBCA" } | Select-Object -First 1
Export-Certificate -Cert $cert -FilePath "$env:TEMP\machine-cert.cer"
Export with private key (PFX — for migration, Admin)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -match "CHLASUBCA" } | Select-Object -First 1
$password = ConvertTo-SecureString -String "ExportP@ss!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "$env:TEMP\machine-cert.pfx" -Password $password
Import certificate to trusted root
Import-Certificate -FilePath "C:\certs\enterprise-root-ca.cer" -CertStoreLocation Cert:\LocalMachine\Root

certutil Quick Reference

certutil commands for field work
certutil -store My                                               (1)
certutil -store My "THUMBPRINT"                                  (2)
certutil -verify "C:\certs\cert.cer"                             (3)
certutil -dump "C:\certs\cert.cer"                               (4)
certutil -pulse                                                  (5)
certutil -config - -ping                                         (6)
certutil -CATemplates                                            (7)
certutil -viewstore "REQUEST"                                    (8)
certutil -repairstore My "THUMBPRINT"                            (9)
1 List all certs in Personal store with full details
2 View specific cert by thumbprint
3 Verify cert file against chain
4 Dump raw cert contents (ASN.1)
5 Trigger auto-enrollment
6 Ping enterprise CA (check availability)
7 List available certificate templates
8 Check pending cert requests
9 Repair private key permissions (Admin)

Machine Certificates (LocalMachine Store)

List all machine certificates (Personal store)
Get-ChildItem Cert:\LocalMachine\My | Select-Object Thumbprint, Subject, NotAfter, Issuer
List machine certs with expiration status
Get-ChildItem Cert:\LocalMachine\My | Select-Object Subject, NotAfter,
    @{N='DaysLeft';E={($_.NotAfter - (Get-Date)).Days}},
    @{N='Status';E={if($_.NotAfter -lt (Get-Date)){'EXPIRED'}elseif(($_.NotAfter - (Get-Date)).Days -lt 30){'EXPIRING'}else{'OK'}}}
Find machine cert by subject (hostname)
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -match $env:COMPUTERNAME }
Find machine cert by thumbprint
Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq "ABC123..."
Find machine certs issued by specific CA
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Issuer -match "Enterprise-CA" }
Find machine certs with Client Authentication EKU (for EAP-TLS)
Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
} | Select-Object Subject, Thumbprint, NotAfter
Get machine cert details (full)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -match $env:COMPUTERNAME } | Select-Object -First 1
$cert | Format-List *
Check machine cert chain validity
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -match $env:COMPUTERNAME } | Select-Object -First 1
$chain = New-Object Security.Cryptography.X509Certificates.X509Chain
$chain.Build($cert)
$chain.ChainStatus | Select-Object Status, StatusInformation
Check machine cert private key exists
Get-ChildItem Cert:\LocalMachine\My | Select-Object Subject, HasPrivateKey, NotAfter |
    Where-Object { $_.Subject -match $env:COMPUTERNAME }
List Trusted Root CAs (machine)
Get-ChildItem Cert:\LocalMachine\Root | Select-Object Subject, Thumbprint, NotAfter | Sort-Object Subject
List Intermediate CAs (machine)
Get-ChildItem Cert:\LocalMachine\CA | Select-Object Subject, Thumbprint, NotAfter | Sort-Object Subject
Find specific Root CA
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match "Enterprise-Root-CA" }
Export machine cert to file (no private key)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -match $env:COMPUTERNAME } | Select-Object -First 1
Export-Certificate -Cert $cert -FilePath "$env:TEMP\machine-cert.cer"
Export machine cert with private key (PFX) - requires admin
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.Subject -match $env:COMPUTERNAME } | Select-Object -First 1
$password = ConvertTo-SecureString -String "ExportP@ss!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "$env:TEMP\machine-cert.pfx" -Password $password
Import certificate to machine store (Admin)
Import-Certificate -FilePath "C:\Certs\ca-root.cer" -CertStoreLocation Cert:\LocalMachine\Root
Import PFX to machine store (Admin)
$password = ConvertTo-SecureString -String "ImportP@ss!" -Force -AsPlainText
Import-PfxCertificate -FilePath "C:\Certs\machine.pfx" -CertStoreLocation Cert:\LocalMachine\My -Password $password
Delete machine certificate (Admin)
$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object Thumbprint -eq "ABC123..."
Remove-Item $cert.PSPath

User Certificates (CurrentUser Store)

List all user certificates (Personal store)
Get-ChildItem Cert:\CurrentUser\My | Select-Object Thumbprint, Subject, NotAfter, Issuer
List user certs with expiration status
Get-ChildItem Cert:\CurrentUser\My | Select-Object Subject, NotAfter,
    @{N='DaysLeft';E={($_.NotAfter - (Get-Date)).Days}},
    @{N='Status';E={if($_.NotAfter -lt (Get-Date)){'EXPIRED'}elseif(($_.NotAfter - (Get-Date)).Days -lt 30){'EXPIRING'}else{'OK'}}}
Find user cert by UPN/email
Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -match $env:USERNAME }
Find user certs with Client Authentication EKU
Get-ChildItem Cert:\CurrentUser\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
} | Select-Object Subject, Thumbprint, NotAfter
Find user certs with Smart Card Logon EKU
Get-ChildItem Cert:\CurrentUser\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Smart Card Logon"
} | Select-Object Subject, Thumbprint, NotAfter
Get user cert SAN (Subject Alternative Name)
$cert = Get-ChildItem Cert:\CurrentUser\My | Select-Object -First 1
$san = $cert.Extensions | Where-Object { $_.Oid.FriendlyName -eq "Subject Alternative Name" }
$san.Format($true)
Check user cert chain validity
$cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -match $env:USERNAME } | Select-Object -First 1
$chain = New-Object Security.Cryptography.X509Certificates.X509Chain
$chain.Build($cert)
$chain.ChainStatus | Select-Object Status, StatusInformation
Check user cert private key exists
Get-ChildItem Cert:\CurrentUser\My | Select-Object Subject, HasPrivateKey, NotAfter
List Trusted Root CAs (user store)
Get-ChildItem Cert:\CurrentUser\Root | Select-Object Subject, Thumbprint | Sort-Object Subject
Export user cert to file (no private key)
$cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -match $env:USERNAME } | Select-Object -First 1
Export-Certificate -Cert $cert -FilePath "$env:TEMP\user-cert.cer"
Export user cert with private key (PFX)
$cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.Subject -match $env:USERNAME } | Select-Object -First 1
$password = ConvertTo-SecureString -String "ExportP@ss!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath "$env:TEMP\user-cert.pfx" -Password $password
Import certificate to user store
Import-Certificate -FilePath "C:\Certs\ca-root.cer" -CertStoreLocation Cert:\CurrentUser\Root
Import PFX to user store
$password = ConvertTo-SecureString -String "ImportP@ss!" -Force -AsPlainText
Import-PfxCertificate -FilePath "C:\Certs\user.pfx" -CertStoreLocation Cert:\CurrentUser\My -Password $password
Delete user certificate
$cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object Thumbprint -eq "ABC123..."
Remove-Item $cert.PSPath

802.1X / EAP-TEAP Certificate Validation

Validate machine cert for EAP-TLS (Client Auth EKU + valid chain)
$certs = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication" -and
    $_.NotAfter -gt (Get-Date) -and
    $_.HasPrivateKey
}
foreach ($cert in $certs) {
    $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
    $valid = $chain.Build($cert)
    [PSCustomObject]@{
        Subject = $cert.Subject
        Thumbprint = $cert.Thumbprint
        Expires = $cert.NotAfter
        ChainValid = $valid
        ChainErrors = ($chain.ChainStatus | ForEach-Object { $_.Status }) -join ","
    }
}
Validate user cert for EAP-TLS
$certs = Get-ChildItem Cert:\CurrentUser\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication" -and
    $_.NotAfter -gt (Get-Date) -and
    $_.HasPrivateKey
}
foreach ($cert in $certs) {
    $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
    $valid = $chain.Build($cert)
    [PSCustomObject]@{
        Subject = $cert.Subject
        Thumbprint = $cert.Thumbprint
        Expires = $cert.NotAfter
        ChainValid = $valid
    }
}
Check if ISE/RADIUS server cert is trusted
$iseCA = "Enterprise-Root-CA"  # Your ISE cert issuer
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match $iseCA } |
    Select-Object Subject, Thumbprint, NotAfter
Get certificate EKU OIDs
$cert = Get-ChildItem Cert:\LocalMachine\My | Select-Object -First 1
$cert.EnhancedKeyUsageList | Select-Object FriendlyName, ObjectId
# Client Auth: 1.3.6.1.5.5.7.3.2
# Server Auth: 1.3.6.1.5.5.7.3.1
# Smart Card Logon: 1.3.6.1.4.1.311.20.2.2
Check certificate template used
$cert = Get-ChildItem Cert:\LocalMachine\My | Select-Object -First 1
$templateExt = $cert.Extensions | Where-Object { $_.Oid.Value -eq "1.3.6.1.4.1.311.21.7" }
if ($templateExt) {
    $templateExt.Format($true)
}
Full 802.1X machine cert diagnostic
Write-Host "=== Machine Certificates for 802.1X ===" -ForegroundColor Cyan
$certs = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
}
if (-not $certs) {
    Write-Host "ERROR: No Client Authentication certificates found!" -ForegroundColor Red
} else {
    foreach ($cert in $certs) {
        Write-Host "`nSubject: $($cert.Subject)" -ForegroundColor Yellow
        Write-Host "Thumbprint: $($cert.Thumbprint)"
        Write-Host "Issuer: $($cert.Issuer)"
        Write-Host "Expires: $($cert.NotAfter) ($(($cert.NotAfter - (Get-Date)).Days) days)"
        Write-Host "Has Private Key: $($cert.HasPrivateKey)"

        $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
        $chainValid = $chain.Build($cert)
        Write-Host "Chain Valid: $chainValid"
        if (-not $chainValid) {
            $chain.ChainStatus | ForEach-Object { Write-Host "  Chain Error: $($_.Status)" -ForegroundColor Red }
        }
    }
}
Full 802.1X user cert diagnostic
Write-Host "=== User Certificates for 802.1X ===" -ForegroundColor Cyan
$certs = Get-ChildItem Cert:\CurrentUser\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
}
if (-not $certs) {
    Write-Host "ERROR: No Client Authentication certificates found!" -ForegroundColor Red
} else {
    foreach ($cert in $certs) {
        Write-Host "`nSubject: $($cert.Subject)" -ForegroundColor Yellow
        Write-Host "Thumbprint: $($cert.Thumbprint)"
        Write-Host "Issuer: $($cert.Issuer)"
        Write-Host "Expires: $($cert.NotAfter) ($(($cert.NotAfter - (Get-Date)).Days) days)"
        Write-Host "Has Private Key: $($cert.HasPrivateKey)"

        $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
        $chainValid = $chain.Build($cert)
        Write-Host "Chain Valid: $chainValid"
    }
}

Certificate Enrollment (AD CS / Auto-Enrollment)

Check auto-enrollment status
gpresult /h "$env:TEMP\gpresult.html" /f
Start-Process "$env:TEMP\gpresult.html"
Trigger machine certificate auto-enrollment
certutil -pulse
Trigger user certificate auto-enrollment
certreq -enroll -q
Force Group Policy update (triggers auto-enrollment)
gpupdate /force
List available certificate templates
certutil -CATemplates
Request certificate from specific template (interactive)
certreq -enroll -q "Workstation Authentication"
Create CSR (Certificate Signing Request)
$inf = @"
[Version]
Signature = "`$Windows NT`$"

[NewRequest]
Subject = "CN=$env:COMPUTERNAME.$env:USERDNSDOMAIN"
KeySpec = 1
KeyLength = 2048
Exportable = TRUE
MachineKeySet = TRUE
SMIME = FALSE
PrivateKeyArchive = FALSE
UserProtected = FALSE
UseExistingKeySet = FALSE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
ProviderType = 12
RequestType = PKCS10
KeyUsage = 0xa0

[EnhancedKeyUsageExtension]
OID = 1.3.6.1.5.5.7.3.2  ; Client Authentication
"@
$inf | Out-File "$env:TEMP\cert-request.inf"
certreq -new "$env:TEMP\cert-request.inf" "$env:TEMP\cert-request.csr"
Submit CSR to CA
certreq -submit -config "CA-SERVER\Enterprise-CA" "$env:TEMP\cert-request.csr" "$env:TEMP\cert-response.cer"
Install certificate response
certreq -accept "$env:TEMP\cert-response.cer"
Check pending certificate requests
certutil -viewstore "REQUEST"

WLAN Profile Certificate Configuration

Inspect Enterprise WiFi Profiles

View 802.1X WiFi profile — verify EAP-TEAP config
netsh wlan show profile name="CHLA_Staff"                        (1)
netsh wlan show profile name="CHLA-Remote"                       (2)
1 Look for: Authentication: WPA2-Enterprise, EAP type: Microsoft: Tunnel EAP (TEAP), 802.1X: Enabled, 802.1X auth credential: Machine or user credential
2 Check Cache user information — CHLA-Remote has No, meaning fresh auth every connect
Compare Group Policy vs User profiles
netsh wlan show profiles
# Group Policy profiles (read only) — pushed by GPO, can't delete locally
# User profiles — locally created, can export/delete

Export and Parse EAP-TEAP XML Config

Export enterprise profile to XML for analysis
netsh wlan export profile name="CHLA_Staff" folder="$env:TEMP" key=clear
Parse the OneX / EAP-TEAP configuration from exported XML
[xml]$profile = Get-Content "$env:TEMP\Wi-Fi-CHLA_Staff.xml"

# Authentication method
$auth = $profile.WLANProfile.MSM.security.authEncryption
Write-Host "Authentication: $($auth.authentication)"
Write-Host "Cipher: $($auth.encryption)"
Write-Host "Use 802.1X: $($auth.useOneX)"

# EAP config (TEAP Type=55, TLS Type=13, MSCHAPv2 Type=26)
$eapConfig = $profile.WLANProfile.MSM.security.OneX.EAPConfig
if ($eapConfig) {
    Write-Host "`nEAP Configuration:"
    $eapConfig.OuterXml
}
Check EAP method type in profile
[xml]$profile = Get-Content "$env:TEMP\Wi-Fi-CHLA_Staff.xml"
$eapType = $profile.WLANProfile.MSM.security.OneX.EAPConfig.EapHostConfig.EapMethod.Type.'#text'
$eapName = switch ($eapType) {
    '13'  { 'EAP-TLS (certificate only)' }
    '21'  { 'EAP-TTLS' }
    '25'  { 'PEAP' }
    '26'  { 'MSCHAPv2' }
    '55'  { 'EAP-TEAP (tunnel, multiple inner methods)' }
    default { "Unknown ($eapType)" }
}
Write-Host "EAP Method: $eapName (Type $eapType)"

Wired 802.1X Profile Inspection

View and export wired 802.1X profile
netsh lan show profiles interface="Ethernet"
netsh lan export profile folder="$env:TEMP" interface="Ethernet"
Parse wired profile XML
$wiredProfiles = Get-ChildItem "$env:TEMP" -Filter "Ethernet*.xml"
if ($wiredProfiles) {
    [xml]$wired = Get-Content $wiredProfiles[0].FullName
    $wired.LANProfile.MSM.security | Format-List
}

Verify Cert Matches Profile Requirements

Check machine cert against EAP-TEAP requirements
Write-Host "=== EAP-TEAP requires ===" -ForegroundColor Cyan
Write-Host "1. Machine cert with Client Authentication EKU"
Write-Host "2. Issued by trusted CA (in Root/CA stores)"
Write-Host "3. Private key present"
Write-Host "4. Not expired"
Write-Host "5. Trust chain complete (Root CA → Sub CA → Machine cert)"

$cert = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication" -and
    $_.HasPrivateKey -and
    $_.NotAfter -gt (Get-Date)
} | Select-Object -First 1

if ($cert) {
    Write-Host "`nCandidate cert found:" -ForegroundColor Green
    Write-Host "  Subject: $($cert.Subject)"
    Write-Host "  Issuer:  $($cert.Issuer)"
    Write-Host "  Expires: $($cert.NotAfter) ($(($cert.NotAfter - (Get-Date)).Days) days)"

    $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
    $valid = $chain.Build($cert)
    Write-Host "  Chain:   $(if($valid){'VALID'}else{'BROKEN'})" -ForegroundColor $(if($valid){'Green'}else{'Red'})
} else {
    Write-Host "`nNO VALID CERT FOUND — EAP-TEAP will fail" -ForegroundColor Red
    Write-Host "Run: certutil -pulse   (to trigger auto-enrollment)"
}

EAP Type Reference

Type Name Used For

13

EAP-TLS

Certificate-only auth (machine or user cert)

21

EAP-TTLS

Tunneled TLS (username/password inside TLS tunnel)

25

PEAP

Protected EAP (MSCHAPv2 inside TLS tunnel)

26

MSCHAPv2

Password auth (inner method for PEAP/TEAP)

55

EAP-TEAP

Tunnel EAP — supports multiple inner methods (TLS + MSCHAPv2), machine + user chaining

EAP-TEAP (Type 55) is the modern replacement for PEAP. It supports:

  • Machine auth (TLS with machine cert) + User auth (TLS with user cert or MSCHAPv2)

  • Chaining — authenticate machine identity AND user identity in one session

  • This is what CHLA_Staff and CHLA-Remote use: 802.1X auth credential: Machine or user credential

Certificate Troubleshooting

Open Certificate Manager (MMC snap-in)
certmgr.msc        # User certs
certlm.msc         # Machine certs (Admin)
Check certificate services status
certutil -ping
Check CA availability
certutil -config - -ping
View certificate in detail (by thumbprint)
certutil -store My "THUMBPRINT_HERE"
Verify certificate file
certutil -verify "C:\Certs\cert.cer"
Dump certificate file contents
certutil -dump "C:\Certs\cert.cer"
Check private key container
$cert = Get-ChildItem Cert:\LocalMachine\My | Select-Object -First 1
$cert.PrivateKey.CspKeyContainerInfo | Select-Object *
Repair private key permissions (Admin)
certutil -repairstore My "THUMBPRINT_HERE"
List all certificate stores
Get-ChildItem Cert:\LocalMachine
Get-ChildItem Cert:\CurrentUser
Find expired certificates (all stores)
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.NotAfter -lt (Get-Date) } |
    Select-Object Subject, NotAfter, Thumbprint
Get-ChildItem Cert:\CurrentUser\My | Where-Object { $_.NotAfter -lt (Get-Date) } |
    Select-Object Subject, NotAfter, Thumbprint
Find certificates expiring in 30 days
$threshold = (Get-Date).AddDays(30)
Get-ChildItem Cert:\LocalMachine\My | Where-Object { $_.NotAfter -lt $threshold -and $_.NotAfter -gt (Get-Date) } |
    Select-Object Subject, NotAfter, @{N='DaysLeft';E={($_.NotAfter - (Get-Date)).Days}}
Clear SSL state (helps with cert selection issues)
# Opens Internet Options, go to Content > Clear SSL State
rundll32.exe inetcpl.cpl,ClearMyTracksByProcess 32
Reset 802.1X authentication state
netsh lan reconnect interface="Ethernet"
netsh wlan reconnect interface="Wi-Fi"
Check SCHANNEL event logs (TLS handshake failures)
Get-WinEvent -LogName "System" -MaxEvents 100 |
    Where-Object { $_.ProviderName -eq "Schannel" } |
    Select-Object TimeCreated, LevelDisplayName,
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
Check CAPI2 event logs (certificate chain validation)
# Enable CAPI2 logging first (one-time, Admin):
wevtutil set-log Microsoft-Windows-CAPI2/Operational /enabled:true

# Read certificate validation events:
Get-WinEvent -LogName "Microsoft-Windows-CAPI2/Operational" -MaxEvents 20 |
    Select-Object TimeCreated, @{N='Summary';E={$_.Message.Split("`n")[0]}} |
    Format-Table -Wrap

802.1X Authentication Event Logs

WiFi 802.1X events (EAP-TEAP authentication)
Get-WinEvent -LogName "Microsoft-Windows-WLAN-AutoConfig/Operational" -MaxEvents 50 |
    Where-Object { $_.Id -in @(8001,8002,8003,11001,11002,11004,11010) } |
    Select-Object TimeCreated, Id,
    @{N='Event';E={switch($_.Id){8001{'Connected'}8002{'ConnFailed'}8003{'Disconnected'}11001{'AuthStarted'}11002{'AuthSuccess'}11004{'AuthFailed'}11010{'NotAvailable'}}}},
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
Wired 802.1X events
Get-WinEvent -LogName "Microsoft-Windows-Wired-AutoConfig/Operational" -MaxEvents 50 |
    Where-Object { $_.Id -in @(15500,15501,15503) } |
    Select-Object TimeCreated, Id,
    @{N='Event';E={switch($_.Id){15500{'AuthStarted'}15501{'AuthSuccess'}15503{'AuthFailed'}}}},
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
Filter for failures only (last 24 hours)
$since = (Get-Date).AddHours(-24)
Get-WinEvent -LogName "Microsoft-Windows-WLAN-AutoConfig/Operational" |
    Where-Object { $_.TimeCreated -gt $since -and $_.Id -in @(8002,11004) } |
    Select-Object TimeCreated, Id, Message | Format-List
NPS/RADIUS events (if running NPS locally — rare on workstations)
Get-WinEvent -LogName "Security" -MaxEvents 100 |
    Where-Object { $_.Id -in @(6272,6273,6274,6278) } |
    Select-Object TimeCreated, Id,
    @{N='Event';E={switch($_.Id){6272{'NPS Granted'}6273{'NPS Denied'}6274{'NPS Discarded'}6278{'NPS Extension DLL'}}}},
    @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap

Full 802.1X Diagnostic Script

One-shot diagnostic — run when a workstation can’t authenticate
Write-Host "`n=== dot3svc / Wlansvc Status ===" -ForegroundColor Cyan
Get-Service -Name dot3svc, Wlansvc | Select-Object Name, Status, StartType | Format-Table

Write-Host "=== Machine Cert (Client Auth EKU) ===" -ForegroundColor Cyan
$certs = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
    $_.EnhancedKeyUsageList.FriendlyName -contains "Client Authentication"
}
if (-not $certs) { Write-Host "  NO CLIENT AUTH CERT FOUND" -ForegroundColor Red }
else {
    $certs | ForEach-Object {
        $chain = New-Object Security.Cryptography.X509Certificates.X509Chain
        $valid = $chain.Build($_)
        [PSCustomObject]@{
            Subject    = $_.Subject.Substring(0, [Math]::Min(50, $_.Subject.Length))
            Issuer     = $_.Issuer.Substring(0, [Math]::Min(40, $_.Issuer.Length))
            Expires    = $_.NotAfter
            DaysLeft   = ($_.NotAfter - (Get-Date)).Days
            PrivateKey = $_.HasPrivateKey
            ChainValid = $valid
        }
    } | Format-Table
}

Write-Host "=== Enterprise Root CA in Trust Store ===" -ForegroundColor Cyan
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -match "CHLA" } |
    Select-Object Subject, NotAfter | Format-Table

Write-Host "=== Wired 802.1X Status ===" -ForegroundColor Cyan
netsh lan show interfaces

Write-Host "`n=== WiFi 802.1X Status ===" -ForegroundColor Cyan
netsh wlan show interfaces | Select-String -Pattern "State|SSID|Authentication|802.1X"

Write-Host "`n=== Recent Auth Failures (last 24h) ===" -ForegroundColor Cyan
$since = (Get-Date).AddHours(-24)
$failures = @()
try {
    $failures += Get-WinEvent -LogName "Microsoft-Windows-WLAN-AutoConfig/Operational" -ErrorAction SilentlyContinue |
        Where-Object { $_.TimeCreated -gt $since -and $_.Id -in @(8002,11004) }
} catch {}
try {
    $failures += Get-WinEvent -LogName "Microsoft-Windows-Wired-AutoConfig/Operational" -ErrorAction SilentlyContinue |
        Where-Object { $_.TimeCreated -gt $since -and $_.Id -eq 15503 }
} catch {}
if ($failures) {
    $failures | Select-Object TimeCreated, Id, @{N='Summary';E={$_.Message.Split("`n")[0]}} | Format-Table -Wrap
} else { Write-Host "  No auth failures in last 24 hours" -ForegroundColor Green }