When you need to update a certificate you can either update the certificate manually or you can script this via an API call to, in this case Digicert which, means you can update your certificates from the shell after for security confirming the order number with the script.
If you wish to use this you will need to generate yourself an API key which you will need to keep private as with that key anyone can place an order for certificates as well as revoke them, but I have limited the access of the API key to only certain operations.
You will need to guard your API key like you do the private key or a certificate on a Public CA provider, first we need to define two variables, once being you ApiKey and the other being the base URL for the API access as below:
$ApiKey = "B56NR7QK<NOT REAL KEY>ZZAKSJAHSAKJ4343"
$ApiBaseUrl = "https://certcentral.digicert.eu/services/v2"
All public certificate providers have extensive documentation on how to use their API calls, and Digicert is no exception so you can review that to get the correct API syntax which should be publically available.
The script first scans for all the certificates issued by Digicert as you can see below, then you have a choose the certificate you are looking to update:
Re-Issue Flow
Then you need to decide if you want to reissue or place a new order as below:
If you choose to reissue then remember that the existing certificate will be revoked within 72 hours you are warned of this in the script, as a mistake here can override a existing certificate:
This will then confirm you actions and if you agree the API will update the certificate with the same common name and SAN names as before and return the response in the script:
New Order Flow
If you go down the new order flow then you will be asked for the certificate type:
Then you will be asked to confirm your actions, when you agree the API will be called and the new order will be issued:
The Script : IIS-ReneworUpdate.ps1
# DigiCert Certificate Auto-Renewal Script
#You may need to update the SSL options based on what options are enabled in Digicert please click the CertCentral for that information and code types, this will also be in the API documentation library
# Configuration Variables
$ApiKey = "YOUR-API-KEY-HERE" # Replace with your DigiCert API key
$ApiBaseUrl = "https://certcentral.digicert.eu/services/v2" # EU CertCentral Services API endpoint
# Function to write API error
function Write-ApiError {
param(
[Parameter(Mandatory=$true)]
$Error,
[Parameter(Mandatory=$true)]
[string]$Context,
[Parameter(Mandatory=$true)]
[string]$Url
)
Write-Host -ForegroundColor Red "ERROR: $Context"
Write-Host -ForegroundColor Red "URL: $Url"
Write-Host -ForegroundColor Red "Error Message: $($Error.Exception.Message)"
Write-Host -ForegroundColor Red "Error Details: $($Error | Out-String)"
}
# Function to write verbose output
function Write-VerboseOutput {
param(
[string]$Message
)
Write-Verbose $Message -Verbose
}
# Function to get all DigiCert certificates
function Get-DigiCertCertificates {
$certs = Get-ChildItem Cert:\LocalMachine\My | Where-Object {
$_.Issuer -like "*DigiCert*"
}
# Create a custom object array with additional properties while preserving the certificate object $certInfoArray = @()
foreach ($cert in $certs) {
$certInfo = @{
Certificate = $cert
DaysUntilExpiration = (New-TimeSpan -Start (Get-Date) -End $cert.NotAfter).Days
BoundToIIS = [bool](Get-WebBinding | Where-Object {$_.certificateHash -eq $cert.Thumbprint})
Subject = $cert.Subject
NotAfter = $cert.NotAfter
SerialNumber = $cert.SerialNumber
}
$certInfoArray += New-Object PSObject -Property $certInfo
}
return $certInfoArray
}
# Function to get order ID from DigiCert API using certificate details
function Get-OrderIdFromCertificate {
param(
[Parameter(Mandatory=$true)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
)
$headers = @{
'X-DC-DEVKEY' = $ApiKey
'Content-Type' = 'application/json'
}
# Search using the certificate's serial number
$serialNumber = $Certificate.SerialNumber
$url = "$ApiBaseUrl/order/certificate?serial_number=$serialNumber"
try {
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Get
if ($response.orders -and $response.orders.Count -gt 0) {
return $response.orders[0].id
}
}
catch {
Write-ApiError -Error $_ -Context "Getting order ID from certificate" -Url $url
return $null
}
return $null
}
# Function to generate a new CSR using certreq.exe
function New-CertificateSigningRequest {
param(
[Parameter(Mandatory=$true)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
)
try {
# Create a temporary directory for CSR generation
$tempDir = Join-Path $env:TEMP "CSRGen_$(Get-Random)"
New-Item -ItemType Directory -Path $tempDir -Force | Out-Null
# Extract subject components
$subjectComponents = $Certificate.Subject -split ', '
$cn = ($subjectComponents | Where-Object { $_ -like "CN=*" }).Replace("CN=", "")
$o = ($subjectComponents | Where-Object { $_ -like "O=*" }).Replace("O=", "")
$l = ($subjectComponents | Where-Object { $_ -like "L=*" }).Replace("L=", "")
$c = ($subjectComponents | Where-Object { $_ -like "C=*" }).Replace("C=", "")
# Get SANs from the certificate
$sanExt = $Certificate.Extensions | Where-Object { $_.Oid.FriendlyName -eq "Subject Alternative Name" }
$sans = if ($sanExt) {
($sanExt.Format($true) -split "`n" | Where-Object { $_ -like "DNS Name=*" }).Replace("DNS Name=", "").Trim()
} else {
@()
}
# Create INF file content
$infContent = @"
[Version]
Signature="$Windows NT$"
[NewRequest]
Subject = "CN=$cn, O=$o, L=$l, C=$c"
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
[Extensions]
2.5.29.17 = "{text}"
_continue_ = "DNS=$cn"
"@
# Add additional SANs
foreach($san in $sans | Where-Object { $_ -ne $cn }) {
$infContent += "`n_continue_ = `"DNS=$san`""
}
# Save INF file
$infPath = Join-Path $tempDir "request.inf"
$infContent | Out-File -FilePath $infPath -Encoding ascii
# Generate CSR
$csrPath = Join-Path $tempDir "request.csr"
$result = certreq -new $infPath $csrPath
if (Test-Path $csrPath) {
$csr = Get-Content -Path $csrPath -Raw
# Clean up the CSR content
$csr = $csr -replace "-----BEGIN NEW CERTIFICATE REQUEST-----", ""
$csr = $csr -replace "-----END NEW CERTIFICATE REQUEST-----", ""
$csr = $csr.Trim()
# Clean up temp files
Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue
return $csr
} else {
throw "Failed to generate CSR"
}
}
catch {
Write-ApiError -Error $_ -Context "Generating CSR" -Url "N/A"
throw
}
}
# Function to initiate certificate renewal
function Start-CertificateRenewal {
param(
[Parameter(Mandatory=$true)]
[string]$OrderId,
[Parameter(Mandatory=$true)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
)
Write-VerboseOutput "Initiating renewal for order ID: $OrderId"
$headers = @{
'X-DC-DEVKEY' = $ApiKey
'Content-Type' = 'application/json'
}
try {
# Generate CSR
$csr = New-CertificateSigningRequest -Certificate $Certificate
# Create renewal request body
$commonName = ($Certificate.Subject -split "CN=")[1].Split(',')[0].Trim()
$body = @{
"certificate" = @{
"common_name" = $commonName
"signature_hash" = "sha384"
"csr" = $csr
}
} | ConvertTo-Json -Depth 10
Write-VerboseOutput "Preparing renewal request for CN: $commonName"
$url = "$ApiBaseUrl/order/certificate/$OrderId/reissue"
Write-VerboseOutput "Calling API URL: $url"
$response = Invoke-RestMethod -Uri $url -Headers $headers -Method Post -Body $body
Write-VerboseOutput "API Response: $($response | ConvertTo-Json)"
return $response
}
catch {
Write-ApiError -Error $_ -Context "Initiating certificate renewal" -Url $url
return $null
}
}
# Main script execution
Write-VerboseOutput "Script started - Using DigiCert EU API at $ApiBaseUrl"
# Check if API Key is set
if ($ApiKey -eq "YOUR-API-KEY-HERE") {
Write-VerboseOutput "Error: Please set your DigiCert API key in the script configuration section"
exit
}
# Get all DigiCert certificates
$certList = Get-DigiCertCertificates
if ($certList.Count -eq 0) {
Write-VerboseOutput "No DigiCert certificates found in the Windows Certificate Store"
exit
}
# Display certificate list
Write-VerboseOutput "Found $($certList.Count) DigiCert certificates:"
Write-Host "`nCertificate Details:" -ForegroundColor Green
$certList | Select-Object Subject, NotAfter, DaysUntilExpiration, BoundToIIS, SerialNumber | Format-Table -AutoSize
# Ask user which certificate to use as template
Write-Host "`nAvailable certificates:" -ForegroundColor Green
for ($i = 0; $i -lt $certList.Count; $i++) {
$boundStatus = if ($certList[$i].BoundToIIS) { "Bound to IIS" } else { "Not bound to IIS" }
Write-Host "[$i] $($certList[$i].Subject) (Expires: $($certList[$i].NotAfter)) - $boundStatus"
Write-Host " Serial Number: $($certList[$i].SerialNumber)"
}
$selection = Read-Host "`nEnter the number of the certificate"
# Ask user what action they want to take
Write-Host "`nSelect the action you want to perform:" -ForegroundColor Green
Write-Host "[1] Reissue existing certificate (revokes old certificate)"
Write-Host "[2] Create new certificate order (keeps old certificate active)"
$action = Read-Host "`nEnter your selection (1 or 2)"
switch ($action) {
"1" {
# Display reissue warning and instructions
Write-Host "`n╔════════════════════ IMPORTANT WARNING ════════════════════╗" -ForegroundColor Red
Write-Host "║ ENTERING THE WRONG ORDER ID WILL HAVE SERIOUS CONSEQUENCES ║" -ForegroundColor Red
Write-Host "╠════════════════════════════════════════════════════════════╣" -ForegroundColor Red
Write-Host "║ • The wrong order ID will renew the wrong certificate ║" -ForegroundColor Red
Write-Host "║ • The old certificate will be REVOKED ║" -ForegroundColor Red
Write-Host "║ • Services will STOP WORKING within 72 hours ║" -ForegroundColor Red
Write-Host "╠════════════════════ HOW TO PROCEED ═════════════════════════╣" -ForegroundColor Yellow
Write-Host "║ 1. Log into DigiCert CertCentral ║" -ForegroundColor Yellow
Write-Host "║ 2. Go to 'Orders' -> 'Orders List' ║" -ForegroundColor Yellow
Write-Host "║ 3. Search for the certificate's common name ║" -ForegroundColor Yellow
Write-Host "║ 4. Match the serial number shown above ║" -ForegroundColor Yellow
Write-Host "║ 5. Copy the Order ID from the matching certificate ║" -ForegroundColor Yellow
Write-Host "╚════════════════════════════════════════════════════════════╝" -ForegroundColor Yellow
$orderId = Read-Host "`nEnter the DigiCert Order ID for this certificate"
# Additional confirmation for reissue
Write-Host "`nYou are about to REISSUE the certificate with:"
Write-Host "Certificate Subject: $($certList[$selection].Subject)" -ForegroundColor Cyan
Write-Host "Serial Number: $($certList[$selection].SerialNumber)" -ForegroundColor Cyan
Write-Host "Order ID: $orderId" -ForegroundColor Cyan
Write-Host "`nWARNING: This will revoke the existing certificate!" -ForegroundColor Red
$finalConfirmation = Read-Host "`nAre you absolutely sure these details are correct? (Yes/No)"
if ($finalConfirmation.ToLower() -ne "yes") {
Write-Host "Operation cancelled. Please verify the details and try again." -ForegroundColor Yellow
exit
}
$renewalResult = Start-CertificateRenewal -OrderId $orderId -Certificate $certList[$selection].Certificate
}
"2" {
# Display product types
Write-Host "`nAvailable certificate types:" -ForegroundColor Green
Write-Host "[1] SecureServer SSL/TLS (ssl_secureserver)"
Write-Host "[2] Standard SSL (ssl_plus)"
Write-Host "[3] Multi-Domain SSL (ssl_ev_plus_md)"
Write-Host "[4] Wildcard SSL (wildcard_plus)"
$productChoice = Read-Host "`nEnter the certificate type number"
$productType = switch ($productChoice) {
"1" { "ssl_secureserver" }
"2" { "ssl_plus" }
"3" { "ssl_ev_plus_md" }
"4" { "wildcard_plus" }
default {
Write-Host "Invalid selection" -ForegroundColor Red
exit
}
}
Write-Host "`nYou are about to create a NEW certificate order using:"
Write-Host "Certificate Subject: $($certList[$selection].Subject)" -ForegroundColor Cyan
Write-Host "Product Type: $productType" -ForegroundColor Cyan
$finalConfirmation = Read-Host "`nAre you sure you want to proceed? (Yes/No)"
if ($finalConfirmation.ToLower() -ne "yes") {
Write-Host "Operation cancelled." -ForegroundColor Yellow
exit
}
$renewalResult = Start-NewCertificateOrder -Certificate $certList[$selection].Certificate -ProductType $productType
}
default {
Write-Host "Invalid selection" -ForegroundColor Red
exit
}
}
if ($renewalResult) {
Write-Host -ForegroundColor Green "`nOperation completed successfully!"
Write-Host -ForegroundColor Green "Order ID: $($renewalResult.id)"
Write-Host -ForegroundColor Green "Status: $($renewalResult.status)"
Write-VerboseOutput "Full Response: $($renewalResult | ConvertTo-Json)"
}
else {
Write-Host -ForegroundColor Red "`nOperation failed!
}
Write-VerboseOutput "Script completed"