Last week, I discussed with dear pal Stefan Schörling at http://www.msfaq.se/ regarding some questions about how to do a complete backup and restore of an Enterprise CA in a disaster recovery scenario without using a full machine backup. In other words, how to do a complete backup of only ADCS itself, not the entire operating system. After we were done, Stefan said “well, that wasn’t so difficult”.
The following morning on the train on my way to work, I thought about Stefan’s comment while doing some PowerShell scripting for ADCS. My conclusion from the previous evenings discussion and Stefan’s comments was very simple: backup for ADCS shouldn’t be difficult!
Now, why is backup of ADCS different from backup up a system state backup on server X? Well, three problems with system state backups and ADCS are that a system state backups relatively huge, are quite time consuming and does NOT take everything! Not many people are aware of the fact that system state backups does not backup the private key of the CA if you are running it on 2008/2008R2 (and you should)!
Even if I did write a very popular post about backup and restore for Active Directory Certificate Services last year, I understood after mine and Stefan’s conversation that the issue had not been clarified to the “big mass of non ADCS geeks”.
ADCS is very different from many other servers or services in our corporate network. It is a critical service from a security and identity perspective since possession of the private keys of an Enterprise CA gives us possibilities to impersonate any user or computer in our forest.
Another thing that is quite special with ADCS is that it’s data and configuration is shattered among many different locations both inside and outside the CA:
* You have the CAPolicy.inf in the local %WINDIR% directory which controls the creation and renewal of the CA’s CSR’s, certificates and keys for the CA itself.
* The CA’s certificates are stored in the computers certificate store. Private keys are sometimes stored locally in %systemdrive%\ProgramData\Microsoft\Crypto\Keys and sometimes (and preferably) in an external HSM.
* The ADCS database and it’s log files contains all information about your requests and your issued and revoked certificates (and may contain private keys if you have key archival enabled). Please remember that the ADCS database holds all the information required that ADCS needs to populate into the CRL’s.
* ADCS stores it’s configuration about the CA, such as CDP/AIA/OCSP/etc. locally in the CA’s registry.
* Furthermore, for Enterprise CA’s, we have multiple objects in the Public Key Services container in Active Directory that holds trusts, certificate templates and configuration.
Since all this is quite confusing for a “non ADCS geek” at the first peek, I decided to simply the process of doing complete ADCS backups without doing full machine backups.
I started to write on my ADCS Backup Tool which does a complete backup of the CA’s certificate + private key, database + log files, CAPolicy.inf file and all ADCS configuration that is stored in the registry.
It is a small PowerShell script that just needs two switches to backup the CA. –backupdir and –pfxpassword, both switches are quite self explaining.

The script is creating a backup folder if needed and backup up all data to it. As you might see, I included some “PowerShell Bling” like Write-Progress, etc.
However, if the backup folder is existing, the script will not try to create that one.

I also included a simple counter to keep track of the backup time.
I would like to give my thanks to three people who helped me with this tool.
First of all, I would like to thank my dear, old pal Mattias Åslund at GraFu for his excellency in application development! You have amazing eyes and ideas my friend, but I am sorry, I can’t implement everything that you suggest, even if it is good stuff…
Then I want thank our intern @ work, Fredrik “Le Mumin” Holmström for his very cleaver ideas regarding error code management and that he helped me to implement them!
Last, but not least, I would like to thank my dear friend, and soon colleague, Per “Nimmsis” Sjökvist that beta tested the script and immediately discovered the “pfxpassword bug” in my code.
Thank you guys!
The script can be found below!
// Fredrik “DXter” Jonsson
#ADCS Backup Tool
#Made by Fredrik "DXter" Jonsson (dxter@poweradmin.se) 2011-03-31
#http://www.poweradmin.se
#Get input strings
param(
[string]$backupdir=$(throw "Mandatory parameter -backupdir missing, for example ""C:\Backup"""),
[string]$pfxpassword=$(throw "Mandatory parameter -pfxpassword missing, for example ""secretpassword""")
)
#Start stopwatch
$totalTime = New-Object -TypeName System.Diagnostics.Stopwatch
$totalTime.Start()
#Set variables
$CAPOLICY = "$ENV:SystemRoot\CAPolicy.inf"
$CERTUTIL = "$ENV:SystemRoot\System32\certutil.exe"
$REG = "$ENV:SystemRoot\System32\reg.exe"
$REGFILE= "adcs_registry_backup.reg"
#Credits
Write-Host
Write-Host "ADCS Backup Tool" -ForegroundColor "Yellow"
Write-Host "by Fredrik ""DXter"" Jonsson (dxter@poweradmin.se)" -ForegroundColor "Yellow"
Write-Host
#Function to backup CA
function Backup-ADCS
{
if (Test-Path –Path $BACKUPDIR)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "Backup directory $BACKUPDIR exists!" -PercentComplete 10
Write-Host "Backup directory $BACKUPDIR exists!" -ForegroundColor Yellow
Start-Sleep -Seconds 1
Write-Host
}
else
{
Write-Host "Creating backup directory $BACKUPDIR!" -ForegroundColor Yellow
New-Item -Path $BACKUPDIR -ItemType Directory
if ($? -eq $true)
{
Write-Host "Backup directory $BACKUPDIR created!" -ForegroundColor Yellow
}
else
{
Write-Host "Backup directory $BACKUPDIR failed to create!" -ForegroundColor Yellow
}
Write-Host
}
#Verify certutil installation
Test-Path $CERTUTIL
if (Test-Path –Path $CERTUTIL)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "Backing up ADCS private key + certificate!" -PercentComplete 20
Write-Host "Backing up ADCS private key + certificate!" -ForegroundColor Yellow
Start-Sleep -Seconds 1
.$CERTUTIL -f -backupKey -p $PFXPASSWORD $BACKUPDIR
if ($? -eq $true)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "ADCS private key + certificate backed up!" -PercentComplete 30
Write-Host "ADCS private key + certificate backed up!" -ForegroundColor Yellow
}
else
{
Write-Host "ADCS private key + certificate not backed up!" -ForegroundColor Red
}
Start-Sleep -Seconds 1
Write-Host
Write-Progress -Activity "ADCS Backup Tool" -Status "Backing up ADCS database + log files!" -PercentComplete 40
Write-Host "Backing up ADCS database + log files!" -ForegroundColor Yellow
Start-Sleep -Seconds 1
.$CERTUTIL -f -backupDB $BACKUPDIR KeepLog
if ($? -eq $true)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "ADCS database + log files backed up!" -PercentComplete 50
Write-Host "ADCS database + log files backed up!" -ForegroundColor Yellow
}
else
{
Write-Host "ADCS database + log files not backed up!" -ForegroundColor Red
}
Start-Sleep -Seconds 1
Write-Host
}
else
{
Write-Host "Certutil not installed!" -ForegroundColor Red
Write-Host
}
#Copy CAPolicy.inf
if (Test-Path –Path $CAPOLICY)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "Backing up CAPolicy.inf" -PercentComplete 60
Write-Host "Backing up CAPolicy.inf!" -ForegroundColor Yellow
Start-Sleep -Seconds 1
Copy-Item $CAPOLICY -Destination $BACKUPDIR\CAPolicy.inf -Force
if ($? -eq $true)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "CAPolicy.inf backed up!" -PercentComplete 70
Write-Host "CAPolicy.inf backed up!" -ForegroundColor Yellow
}
else
{
Write-Host "CAPolicy.inf not backed up!" -ForegroundColor Red
}
Start-Sleep -Seconds 1
Write-Host
}
else
{
Write-Host "CAPolicy.inf does not exist. Skipping!" -ForegroundColor Yellow
Write-Host
}
#Export registry
if (Test-Path –Path $REG,HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "Backing up ADCS configuration from registry!" -PercentComplete 80
Write-Host "Backing up ADCS configuration from registry!" -ForegroundColor Yellow
Start-Sleep -Seconds 1
.$REG export HKLM\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration $BACKUPDIR\$REGFILE /y
if ($? -eq $true)
{
Write-Progress -Activity "ADCS configuration exported from registry!" -Status "ADCS configuration exported from registry!" -PercentComplete 90
Write-Host "ADCS configuration exported from registry!" -ForegroundColor Yellow
}
else
{
Write-Host "ADCS configuration not exported from registry!" -ForegroundColor Red
}
Start-Sleep -Seconds 1
Write-Host
}
else
{
Write-Host "ADCS configuration not existent!" -ForegroundColor Red
Write-Host
}
}
#Run backup
Backup-ADCS | Out-Null
if ($? -eq $true)
{
Write-Progress -Activity "ADCS Backup Tool" -Status "CA backup completed successfully!" -PercentComplete 100
Write-Host "ADCS backup completed successfully!" -ForegroundColor Yellow
Start-Sleep -Seconds 1
Write-Host
}
else
{
Write-Host "ADCS backup not completed successfully!" -ForegroundColor Red
Write-Host
}
#Stop stopwatch
$totalTime.Stop()
$ts = $totalTime.Elapsed
$totalTime = [system.String]::Format("{0:00}:{1:00}:{2:00}",$ts.Hours, $ts.Minutes, $ts.Seconds)
Write-Host "Process total time: $totalTime" -ForegroundColor Yellow
Write-Host