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:
- "Do you wish to overwrite the following file?" for the request file
- "Do you wish to overwrite the following file?" for the certificate file
- "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:
- Using the -q flag: Contrary to some documentation,
certreq -submit -q
doesn't actually suppress all prompts. - Force overwriting with -f flag: This still prompted for some operations.
- 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:
- Test thoroughly in real environments: My initial tests in development missed many of the prompts that appeared in production.
- Don't trust documentation blindly: The
-q
flag didn't work as expected despite documentation suggesting it should. - Consider the full lifecycle: My initial focus on deployment didn't account for renewal and cleanup.
- Sometimes inelegant solutions are necessary: The dialog box automation wasn't pretty, but it was effective.
- Log everything: Detailed logging was essential for troubleshooting issues in deployed scripts.
- 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