Powershell : Automating Computer Authentication Certificates and Certificate Management


I had a particular unique situation where out-of-band I had a requirement deploy and manage computer authentication certificates for our client machines, Down to the fact that these authentication certificates were missing an OID - unfortunately, as the certificates were valid for over one year in some cases, we needed to do a manual update via a script.

What started as a simple task became a technical journey filled with unexpected obstacles, learning opportunities, and solutions I'd like to share.

In this post, I'll walk through my experience of creating an automated certificate deployment process for client computers, along with the struggles of managing certificate duplicates that accumulated over time.

The Challenge: Computer Authentication Certificates

In our environment, every client computer requires a Computer Authentication certificate to securely connect to our corporate resources. Initially, this process was automated and every new device deployment involved generating a certificate request, submitting it to our CA, this was handled my Active Directory - as mentioned earlier, we needed a manual process to temporarily for existing laptops, force a brand new certificate to be issued.

I needed to automate this computer authentication certificate process to make it seamless, reliable, and free of human intervention.

First Attempt: Basic Certificate Request Script

My initial solution was straightforward - a PowerShell script that would generate and submit a certificate request:

$caServer = "ssl-issuer1.bear.local"
$caName = "Bear Issuer A"
$templateName = "RemoteVPN"

# Create certificate request
$infFile = "$env:TEMP\CertRequest\request.inf"
@"
[NewRequest]
KeySpec = 1
KeyLength = 2048
MachineKeySet = TRUE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
RequestType = PKCS10

[RequestAttributes]
CertificateTemplate = $templateName
"@ | Out-File -FilePath $infFile -Encoding ascii

# Submit to CA
certreq -new $infFile "$env:TEMP\CertRequest\request.req"
certreq -submit -config "$caServer\$caName" "$env:TEMP\CertRequest\request.req" 
"$env:TEMP\CertRequest\certificate.cer"
certreq -accept "$env:TEMP\CertRequest\certificate.cer"

But as anyone who's worked with certificate automation knows, the devil is in the details.

The Dialog Box Nightmare

The first major obstacle I encountered was that certreq is far from silent by default. Running the script resulted in multiple popup prompts:

  1. "Do you wish to overwrite the following file?" for the request file
  2. "Do you wish to overwrite the following file?" for the certificate file
  3. "Do you wish to overwrite the following file?" for the response file

These dialog boxes completely broke the automation. Even adding the -Silent = TRUE parameter to the INF file didn't resolve the issue:

[NewRequest]
KeySpec = 1
KeyLength = 2048
MachineKeySet = TRUE
ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
RequestType = PKCS10
Silent = TRUE

I dug into Microsoft's documentation and found that while Silent = TRUE prevents prompts related to smart card PINs, it doesn't stop file overwrite prompts.

The Failed Solutions

I tried several approaches that didn't fully solve the problem:

  1. Using the -q flag: Contrary to some documentation, certreq -submit -q doesn't actually suppress all prompts.
  2. Force overwriting with -f flag: This still prompted for some operations.
  3. Pre-deleting files: I added code to remove existing files before creating new ones:

# Clean up existing files
if (Test-Path "$env:TEMP\CertRequest\request.req") {
    Remove-Item "$env:TEMP\CertRequest\request.req" -Force
}
if (Test-Path "$env:TEMP\CertRequest\certificate.cer") {
    Remove-Item "$env:TEMP\CertRequest\certificate.cer" -Force
}

But even with these measures, some prompts still appeared. It seemed certreq was creating temporary files I wasn't catching.

The Working Solution: Automating Dialog Box Handling

After much experimentation, I realized I needed a completely different approach. Instead of trying to prevent the dialog boxes, I would programmatically respond to them. This led to a solution using Windows Forms to detect and interact with popup windows:

# Setup a PowerShell script to handle the certreq command silently
$autoAcceptScript = @"
Add-Type -AssemblyName System.Windows.Forms
`$wshell = New-Object -ComObject wscript.shell

# Function to check if a dialog exists and accept it
function Accept-Dialog {
    `$dialogExists = `$false
    
    # Try to find and accept the dialog
    for (`$i = 0; `$i -lt 10; `$i++) {
        try {
            `$dialog = [System.Windows.Forms.Form]::FromHandle
       ([System.Diagnostics.Process]::GetProcessesByName('certreq')[0].MainWindowHandle)
            if (`$dialog) {
                `$wshell.AppActivate(`$dialog.Text)
                Start-Sleep -Milliseconds 500
                `$wshell.SendKeys("%Y")  # Alt+Y to select "Yes"
                `$dialogExists = `$true
                break
            }
        } catch {}
        
        Start-Sleep -Milliseconds 1000
    }
    
    return `$dialogExists
}
"@

# Launch the dialog accepter in the background
Start-Process powershell.exe -ArgumentList "-ExecutionPolicy Bypass 
-File `"$autoAcceptScriptPath`"" -WindowStyle Hidden

This approach runs a background PowerShell process that watches for and automatically accepts any certreq dialog boxes. It's not elegant, but it works reliably.

The Certificate Duplication Problem

With my automation in place, I deployed it across our environment. However, after several months, I discovered a new issue: duplicate computer authentication certificates were accumulating in the certificate store. Each time a machine renewed its certificate, the old ones weren't being properly removed.

When examining the certificate store, I found machines with 5+ certificates for the same purpose:

Get-ChildItem -Path Cert:\LocalMachine\My | 
    Where-Object {$_.Subject -like "*$env:COMPUTERNAME*" -and $_.EnhancedKeyUsageList 
    -like "*Remote Access VPN*"} | 
    Format-Table Subject, NotBefore, NotAfter, Thumbprint

The output showed multiple certificates with the same subject and purpose but different validity periods:

Subject                        NotBefore             NotAfter              Thumbprint
-------                        ---------             --------              ----------
CN=BearLaptop.bear.local       1/15/2023 3:45:12 PM  1/15/2024 3:45:12 PM  A1B2C3D4E5F6...
CN=BearLaptop.bear.local       6/22/2023 9:12:03 AM  6/22/2024 9:12:03 AM  F6E5D4C3B2A1...
CN=BearLaptop.bear.local       8/05/2023 11:30:42 AM 8/05/2024 11:30:42 AM B1C2D3E4F5A6...
CN=BearLaptop.bear.local       12/12/2023 2:15:30 PM 12/12/2024 2:15:30 PM E5F6A1B2C3D4...

This duplication resulted in:

  • Increased certificate store size
  • Confusion about which certificate was currently active
  • Potential certificate selection errors by applications/users

Cleaning Up the Certificate Store

The solution required identifying and removing outdated certificates while preserving the newest valid one:

# Find all VPN certificates for this computer
$vpnCerts = Get-ChildItem -Path Cert:\LocalMachine\My | 
    Where-Object {
        $_.Subject -like "*$env:COMPUTERNAME*" -and 
        $_.EnhancedKeyUsageList.FriendlyName -contains "Remote Access VPN"
    }

# Keep the newest valid certificate
$newestCert = $vpnCerts | Sort-Object NotAfter -Descending | Select-Object -First 1

# Remove all other certificates
foreach ($cert in $vpnCerts) {
    if ($cert.Thumbprint -ne $newestCert.Thumbprint) {
        "Removing duplicate certificate: $($cert.Thumbprint), issued on $($cert.NotBefore)" 
| Out-File $logFile -Append
        Remove-Item -Path "Cert:\LocalMachine\My\$($cert.Thumbprint)" -Force
    }
}

I incorporated this cleanup routine into my certificate renewal script to maintain a clean certificate store.

Lessons Learned

This journey taught me several valuable lessons about certificate automation:

  1. Test thoroughly in real environments: My initial tests in development missed many of the prompts that appeared in production.
  2. Don't trust documentation blindly: The -q flag didn't work as expected despite documentation suggesting it should.
  3. Consider the full lifecycle: My initial focus on deployment didn't account for renewal and cleanup.
  4. Sometimes inelegant solutions are necessary: The dialog box automation wasn't pretty, but it was effective.
  5. Log everything: Detailed logging was essential for troubleshooting issues in deployed scripts.
  6. Understand certificate usage context: Computer authentication certificates have different requirements than user certificates, particularly regarding the MachineKeySet parameter and running with the proper system context.

Conclusion

What began as a seemingly simple task of automating computer authentication certificate requests turned into a deep dive into Windows certificate management, dialog automation, and certificate lifecycle management. Despite the challenges, the final solution has been running reliably for months, eliminating manual certificate management tasks and ensuring our client computers always have valid, non-duplicated certificates.

The complete script now handles the following operations:

  • Automatic certificate requests
  • Silent handling of all dialog boxes
  • Cleanup of obsolete certificates
  • Detailed logging for troubleshooting
Previous Post Next Post

نموذج الاتصال