DecommissionCA.ps1


<#PSScriptInfo
 
.VERSION 1.2
 
.GUID 4ffb77b7-00d7-4f55-88a4-4926feea41e8
 
.AUTHOR scaron@pcevolution.com
 
.COMPANYNAME PC-�volution enr.
 
.COPYRIGHT Copyright (c) 2019 PC-�volution enr. This code is licensed under the GNU General Public License (GPL).
 
.TAGS decommission CA SBS 2008 SBS 2011 Windows Server 2012 R2 Essentials Windows Server 2012 R2 Essentials
 
.LICENSEURI
 
.PROJECTURI
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES PSPKI,Windows Management Framework 5.1
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
This script is an aid in decommissioning a Certification Authority (CA).
It allows to save the CA Root Certificate and Key to a location of your choice. All active certificates are revoked and a Certificate Revocation List (CRL) is published with an expiration date later than the expiration date of any certificate published by this CA.
Dependencies: you must install PSPKI (also available on the PowerShell Gallery).
Caution: In theory, this script can be executed against a remote CA. This has not been tested and saving the CA Root Certificate is restricted to the local machine.
 
#>


<#
 
.DESCRIPTION
 Aid in decommissionning a Certification Authority
 
#>
 
Param()


##******************************************************************
## Revision date: 2019.10.08
##
## Copyright (c) 2019 PC-�volution enr.
## This code is licensed under the GNU General Public License (GPL).
##
## THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
## ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
## IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
## PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
##
##******************************************************************

## Do we have a toolset?
Try {
    Write-Host "Please wait..."
    import-module pspki -ErrorAction Stop
    }
Catch {
    Write-Host ""
    Write-Host "Error: Please install the PowerShell PKI Module from PKI Solutions inc."
    Write-Host " (https://www.pkisolutions.com/tools/pspki/)"
    Write-Host " You may also have to install Windows Management Framework 5.1."
    Write-Host ""
    Exit 911
    }

## Enumerate all Certification Authorities and display their status
Try {
    [System.Object[]] $CAs = Get-CertificationAuthority -Error Stop
    $CAs | Get-EnterprisePKIHealthStatus | ft *
    }
Catch {
    Write-Host "There are no Certification Authority active on this network."
    Exit 911
    }

## Get the retiring Certification Authority
If ($CAs.Count -eq 1) {
    $RetiringCA = $CAs[0]
    }
Else {  Do { $Candidate = Read-Host "Please enter 0-based index of retiring Certification Authority" }
          While ($Candidate -lt 0 -or $Candidate -ge $CAs.Count)
        $RetiringCA = $CAs[$Candidate]
     }

## Get access to the Root Certificate on the Certification Authority server
$LocalMachine = ([System.Net.Dns]::GetHostByName(($env:computerName))).HostName
If ($RetiringCA.ComputerName -eq $LocalMachine) {
    $CertToExport = Get-ChildItem -Path cert:\LocalMachine\My | where { $_.SerialNumber -eq $RetiringCA.Certificate.SerialNumber }
    If ($CertToExport.Count -eq 1) {
        ## Get destination folder
        [System.Reflection.Assembly]::LoadWithPartialName("System.windows.forms") | Out-Null
        $FolderName = New-Object System.Windows.Forms.FolderBrowserDialog
        $FolderName.Description = "Select folder to export Root Certification Authority Certificate and Key "
        $FolderName.rootfolder = "MyComputer"

        ## Warn the user if we are not saving the root certificate and key
        if ($FolderName.ShowDialog() -eq "OK") {

            ## Create the full path for this certificate
            $CertOutputFileName = $CertToExport.DnsNameList[0].Unicode
            $CertFullPath = Join-Path -Path $FolderName.SelectedPath -ChildPath "$CertOutputFileName.pfx"

            ## Get a confirmed password (or die!)
            DO {
                $SecurePassword = Read-Host -Prompt "Enter password for $CertFullPath" -AsSecureString
                $ConfirmationPassword = Read-Host -Prompt "Confirm password" -AsSecureString
               } While ( [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($SecurePassword)) `
                        -cne [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($ConfirmationPassword)) )

            # Export PFX certificate along with private key
            Export-PfxCertificate -Cert $CertToExport -FilePath $CertFullPath -Password $SecurePassword -Verbose
        }
        Else {
            Write-Host "Caution: You have elected not to save the Root Certificate and Key."
            Write-Host " Proceed at your own risk."
            }
        }
    Else {
        Write-Host "Caution: Unable to export $RetiringCA Root Certificate and Key"
        Write-Host " Proceed at your own risk."
        }
    }
Else {
    Write-Host "Caution: $RetiringCA is a remote system. Proceed at your risk."
    }

## Get explicit confirmation from the user
Write-Host
Write-Host "Caution: This script irreversibly revokes everything issued by this Certification Authority."
Write-Host " This includes Disaster Recovery Agents and other critical roles."
Write-Host " Upon successfull execution, local operations are still enabled on the CA: if need be,"
Write-Host " you can rerun this script locally."
Write-Host
If ( (Read-Host "Please confirm retirement of" $RetiringCA.DisplayName "[Enter IDO, anything else exits]") -ne "IDO")
  { Exit 911 }

## Set a timestamp for this run
$RevocationDate = Get-Date

## Freeze the retiring CA operations
$RetiringCA | Get-InterfaceFlag | Enable-InterfaceFlag -Flag "NoRemoteICertRequest", "NoLocalICertRequest", "NoRPCICertRequest" -RestartCA
Start-Sleep -s 15

## Deny pending request
[System.Object[]] $PendingCertificates = $RetiringCA | Get-PendingRequest
If ($PendingCertificates.Count -ge 1) {
    $PendingCertificates | Deny-CertificateRequest
    Write-Host "Notice: some pending certificate requests denied."
    }

## Get oldest expiration date of revoked certificates
[System.Object[]] $RevokedCertificates = $RetiringCA | Get-RevokedRequest -Property NotAfter
If ($RevokedCertificates.Count -ge 1) {
    $TargetExpirationDate = ($RevokedCertificates | Measure-Object -Property NotAfter -Maximum).Maximum
    }
Else {
    $TargetExpirationDate = $RevocationDate
    }

## Revoke all outstanding certificates
[System.Object[]] $ActiveCertificates = $RetiringCA | Get-IssuedRequest
If ($ActiveCertificates.Count -ge 1) {
    $ExpirationDate = ($ActiveCertificates | Measure-Object -Property NotAfter -Maximum).Maximum
    If ($ExpirationDate -lt $RevocationDate) {
        Write-Host "Notice: All outstanding certificates are already expired."
        $ExpirationDate = $RevocationDate
        }
    $ActiveCertificates | Revoke-Certificate -Reason "CeaseOfOperation" -RevocationDate $RevocationDate
    }
else {
    Write-Host "Notice: There are no outstanding certificates."
    $ExpirationDate = $RevocationDate
    }

## Compute a lifetime for the next Certificate Revocation List
If ($TargetExpirationDate -gt $ExpirationDate) {
    $Duration = ($TargetExpirationDate - $RevocationDate).Days + 1
    }
Else {
    $Duration = ($ExpirationDate - $RevocationDate).Days + 1
    }

## Update the Certification Authority
Set-CRLValidityPeriod -InputObject (Get-CRLValidityPeriod -CertificationAuthority $RetiringCA) -BaseCRL "$Duration days" -BaseCRLOverlap "2 days" -RestartCA
Start-Sleep -s 15

## Publish a new authoritative CRL
$RetiringCA | Publish-CRL

## Thaw local operations the retiring CA
$RetiringCA | Get-InterfaceFlag | Disable-InterfaceFlag -Flag "NoLocalICertRequest" -RestartCA
Start-Sleep -s 15

Write-Host
Write-Host "It's done. Check the CRL Distribution Points (CDPs). Local operations are still allowed on the retiring CA."
Write-Host