Sometimes I don't understand Microsoft. They a lot of useful things, but thing implementation is quite poor. For example in Windows Server 2008 R2 we have an option to install certificate enrollment service (hereinafter CES) that will allow to securely enroll certificates outside of domain network perimeter. Also CES allows to enroll certificates from non-domain clients. Here is excellent whitepaper about the subject: Certificate Enrollment Web Services in Windows Server 2008 R2. You can setup only one CES instance via Server Manager snap-in. What if we have multiple CA servers and we need to configure CES to work with them? For example, one CA is configured to issue user certificates only and another CA is configured to issue computer certificates only. Also we need to issue these certificates to external clients. In that case we need to setup at least two Windows Server 2008 R2 servers, assign them public IP address and install required CES instance on each CES server. This is pretty ugly. Hopefully there is a trick to install additional CES instances on the same server via CryptoAPI COM interface: CERTOCM.CertificateEnrollmentServerSetup. Currently this interface is not documented on MSDN, therefore I cannot provide interface explanation links. However I wrote PowerShell script that will add additional CES instance and remove specified or all CES instances from local computer. I have commented some code parts for understanding, but the code generally is self-explanatory.

Let's go:

#####################################################################
# AddRemoveCES.ps1
# Version 1.3
#
# Installs or removes Certificate Enrollment Service (CES) instance
#
# Note: Requires Windows Server 2008 R2 Standard/Enterprise/Datacenter.
#
# Vadims Podans (c) 2010
# http://en-us.sysadmins.lv/
#####################################################################
#requires -Version 2.0

function Add-CES {
<#
.Synopsis
    Installs Certificate Enrollment Service instance to local computer
.Description
    This function installs Certificate Enrollment Service instance and configures it
    to work with specified certification authority.
.Parameter CAConfig
    Specifies certification authority configuration string in:
    CAComputerName\CASamitizedName format. CAComputerName may be either
    DNS or NetBIOS name. If this parameter is omitted, CA selection UI will be
    displayed during instance installation.
.Parameter Authentication
    Specifies authentication type for communication. Possible values are:
    Kerberos, UsrPwd or Certificate. Kerberos is used by default.
.Parameter User
    Sets CES AppPool account name. If this parameter is omitted, ApplicationPoolIdentity
    account will be used.
.Parameter Password
    Sets CES AppPool account password.
.Parameter RenewalOnly
    Sets CES service mode to Renewal Only. In that case CES will process certificate
    renewal requests only. No new certificate requests will be accepted.
.EXAMPLE
    Add-CES
    
    Running command without parameters will cause CA selection UI appearance. You will
    need to select CA server for CES server. In addition, default Kerberos authentication
    will be used.
.EXAMPLE
    Add-CES -CAConfig CA1\Contoso-CA -Authentication Certificate -User CustomUser -Password CustomPassword
    
    In this example CES server will be configured to CA server with Contoso-CA name
    and that is hosted on the computer named CA1. CES server will use client
    certificate for authentication and IIS AppPool will be configured to run
    under CustomUser account that has CustomPassword password.
#>
[CmdletBinding()]
    param(
        [string]$CAConfig,
        [ValidateSet("UsrPwd", "Kerberos", "Certificate")]
        [string]$Authentication = "Kerberos",
        [string]$User,
        [string]$Password,
        [switch]$RenewalOnly
    )

#region Check operating system
    $OS = (Get-WmiObject Win32_OperatingSystem).Caption
    if ($OS -notlike "Microsoft Windows Server 2008 R2*") {
        Write-Warning "Only Windows Server 2008 R2 operating system is supported!"; return
    }
#endregion

#region Check user permissions
# check if user has Enterprise Admins permissions
    $elevated = $false
    foreach ($sid in [Security.Principal.WindowsIdentity]::GetCurrent().Groups) {
        if ($sid.Translate([Security.Principal.SecurityIdentifier]).IsWellKnown([Security.Principal.WellKnownSidType]::AccountEnterpriseAdminsSid)) {
            $elevated = $true
        }
    }
    if (!$elevated) {Write-Warning "You must be logged on with Enterprise Admins permissions!"; return}
#endregion

#region Obtain SSL certificate from local store or enroll new one
    function Get-Cert {
# retrieve current domain name. this suffix is used to construct current computer FQDN
        try {
            $domain = ([System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()).Name
        # if command above generates error, the computer is not a member of any AD domain
        } catch {Write-Warning "Current computer is not a part of any Active Directory domain!"; return}
        $fqdn = $env:COMPUTERNAME + "." + $domain
        $validCerts = @()
        # retrive all certificates from computer store that have private key and subject equals computer FQDN
        $certs = @(Get-ChildItem cert:\localmachine\my | ?{$_.HasPrivateKey -and $_.subject -eq "CN=$fqdn"})
        # loop extensions for EKU extension and check for Server Authentication OID
        foreach ($cert in $certs) {
            $eku = $cert.extensions | ?{$_.oid.value -eq "2.5.29.37"}
            if ($eku) {
                if ($eku.EnhancedKeyUsages | ?{$_.value -eq "1.3.6.1.5.5.7.3.1"}) {
                    # if certificate meet minimum requirements, write it to valid certs collection
                    $validCerts += $cert
                }
            }
        }
        # sort certificates in the collection by NotAfter and select one with the longest
        # validity
        if ($validCerts.count -gt 0) {
            ($validCerts | sort NotAfter | select -Last 1).Thumbprint
        } else {
            # if no valid certificate exist in the local store, enroll fro new one.
            $enrollment = New-Object -ComObject X509Enrollment.CX509enrollment
            # use ProductType of Win32_OperatingSystem class to determine computer role
            # domain Controller or Member Server.
            $ServerType = Get-WmiObject (Win32_OperatingSystem).ProductType
            if ($ServerType -eq 2) {$enrollment.InitializeFromTemplate(0x3, "DomainController")}
            elseif ($ServerType -eq 3) {$enrollment.InitializeFromTemplate(0x3, "Machine")}
            try {$enrollment.Enroll()}
            catch {
                Write-Warning "Unable to enroll SSL certificate. In order to use CES server"
                Write-Warning "you will have to manually obtain SSL certificate and configure"
                Write-Warning "IIS to use this certificate."
                return
            }
            $base64 = $enrollment.Certificate(1)
            $cert = New-Object Security.Cryptography.X509Certificates.X509Certificate2
            $cert.Import($([Convert]::FromBase64String($base64)))
            $cert.Thumbprint
        }
    }
#endregion

    $auth = @{"Kerberos" = 2; "UsrPwd" = 4; "Certificate" = 8}
    # we can use ServerManager module to install CES binaries
    Import-Module ServerManager
    # at first check if CES is already installed
    $status = (Get-WindowsFeature -Name ADCS-Enroll-Web-Svc).Installed
    # if still no, install binaries, otherwise do nothing
    if (!$status) {$retn = Add-WindowsFeature -Name ADCS-Enroll-Web-Svc
        if (!$retn.Success) {
            Write-Warning "Unable to install CES service installation packages due of the following error:"
            Write-Warning $retn.ExitCode
            return
        }
    }
    # instantiate CES COM object
    $CES = New-Object -ComObject CERTOCM.CertificateEnrollmentServerSetup
    $CES.InitializeInstallDefaults()
    # use ICertConfig.GetConfig() to display CA selection UI
    if ($CAConfig -eq "") {
        $config = New-Object -ComObject CertificateAuthority.Config
        try {
            $bstr = $config.GetConfig(1)
        } catch {Write-Warning "There is no available Enterprise Certification Authorities or user canceled operation."; return}
    } else {$bstr = $CAConfig}
    $Thumbprint = $(Get-Cert)
    if ($User) {
        $CES.SetApplicationPoolCredentials($User, $Password)
    }
    $CES.SetProperty(0x1, $bstr)
    $CES.SetProperty(0x2, $auth.$Authentication)
    $CES.SetProperty(0x3, $Thumbprint)
    if ($RenewalOnly) {$CES.SetProperty(0x5, $true)}
    Write-Host "Performing Certificate Enrollment Service installation with the following settings:" `

    `nCA configuration string: $bstr
 `

    `nAuthentication type: $Authentication
 -ForegroundColor Cyan
    if ($RenewalOnly) {Write-Host "Renewal mode: Yes" -ForegroundColor Cyan}
    else {Write-Host "Renewal mode: No" -ForegroundColor Cyan}
    Write-Host "CES server URL: $($CES.GetProperty(0x4))" -ForegroundColor Cyan
    if ($Thumbprint) {Write-Host "SSL certificate thumbprint: $Thumbprint" -ForegroundColor Cyan}
    Write-Host ("-" * 50) `

    `nInstallation
 results `

    `n
("-" * 50) -ForegroundColor Green
    $CES.Install()
    if ($?) {Write-Host "CEP service was successfully installed!" -ForegroundColor Green}
}

function Remove-CES {
<#
.Synopsis
    Removes Certificate Enrollment Service instance from local computer.
.Description
    This function removes Certificate Enrollment Service instance or instances
    if you wish to remove all CES instances from local computer.
.Parameter CAConfig
    Specifies certification authority configuration string in:
    CAComputerName\CASamitizedName format. CAComputerName may be either
    DNS or NetBIOS name. If this parameter is omitted, CA selection UI will be
    displayed during instance removal. If -Force switch is asserted, this paramter
    will be ignored and all CES instances will be removed from local computer.
.Parameter Authentication
    Specifies authentication type to remove for specified instance. Possible values are:
    Kerberos, UsrPwd or Certificate. Kerberos is used by default. This parameter
    may be used if multiple instances are installed to work with the same CA server
    but they uses different authentication types.
.Parameter Force
    Instructs to ignore CAConfig and Authentication parameters and remove all CES
    instances from local computer.
.EXAMPLE
    Remove-CES -CAConfig CA1\Contoso-CA
    
    Will remove all CES instances that was configured for CA server named Contoso-CA
    and that is hosted on CA1 computer.
.EXAMPLE
    Remove-CES -Force
    
    Will remove all CES instances from local computer.
#>
[CmdletBinding()]
    param (
        [string]$CAConfig,
        [ValidateSet("UsrPwd", "Kerberos", "Certificate")]
        [string]$Authentication = "Kerberos",
        [switch]$Force
    )

#region Check operating system
    $OS = (Get-WmiObject Win32_OperatingSystem).Caption
    if ($OS -notlike "Microsoft Windows Server 2008 R2*") {
        Write-Warning "Only Windows Server 2008 R2 operating system is supported!"; return
    }
#endregion

#region User permissions
# check if user has Enterprise Admins permissions
    $elevated = $false
    foreach ($sid in [Security.Principal.WindowsIdentity]::GetCurrent().Groups) {
        if ($sid.Translate([Security.Principal.SecurityIdentifier]).IsWellKnown([Security.Principal.WellKnownSidType]::AccountEnterpriseAdminsSid)) {
            $elevated = $true
        }
    }
    if (!$elevated) {Write-Warning "You must be logged on with Enterprise Admins permissions!"; return}
#endregion

    $auth = @{"Kerberos" = 2; "UsrPwd" = 4; "Certificate" = 8}
    if ($CAConfig -eq "" -and !$Force) {
        $config = New-Object -ComObject CertificateAuthority.Config
        try {
            $bstr = $config.GetConfig(1)
        } catch {Write-Warning "There is no available Enterprise Certification Authorities or user canceled operation."; return}
    }
    elseif ($CAConfig -ne "" -and !$Force){$bstr = $CAConfig}
    else {$bstr = $null; $auth.$Authentication = $null}
    $CES = New-Object -ComObject CERTOCM.CertificateEnrollmentServerSetup
    Write-Host "Performing Certificate Enrollment Service removal with the fillowing settings:" -ForegroundColor Cyan
    if ($bstr -eq $null) {Write-Host "CA configuration string: Any" -ForegroundColor Cyan}
    else {Write-Host "CA configuration string: $bstr" -ForegroundColor Cyan}
    if ($auth.$Authentication -eq $null) {Write-Host "Authentication type: Any" -ForegroundColor Cyan}
    else {Write-Host "Authentication type: $Authentication" -ForegroundColor Cyan}
    if ($Force) {Write-Host "Remove installation packages: Yes" -ForegroundColor Cyan}
    else {write-Host "Remove installation packages: No" -ForegroundColor Cyan}
    Write-Host ("-" * 50) `

    `nRemoval
 results `

    `n
("-" * 50) -ForegroundColor Green
    $CES.Uninstall($bstr, $auth.$Authentication)
    if ($?) {Write-Host "CES service successfully removed!" -ForegroundColor Green}
    if ($Force) {
        Import-Module ServerManager
        $retn = Remove-WindowsFeature -Name ADCS-Enroll-Web-Svc
        if (!$retn.Success) {
            Write-Warning "CES installation package removal failed due of the following error:"
            Write-Warning $retn.ExitCode
        }
        else {Write-Host "CES installation packages are successfully removed!" -ForegroundColor Green}
    }
}

copy and paste the code to PowerShell console and run the following commands:

  • to install first or additional CES instance:
Add-CES

Running command without parameters will cause CA selection UI appearance. You will need to select CA server for CES server. In addition, default Kerberos authentication will be used.

Add-CES CA1\Contoso-CA certificate CustomUser CustomPassword

In this example CES server will be configured to CA server with Contoso-CA name and that is hosted on the computer named CA1. CES server will use client certificate for authentication and IIS AppPool will be configured to run under CustomUser account that has CustomPassword password.

  • to remove CES instance (or all instances):
Remove-CES CA1\Contoso-CA

Will remove all CES instances that was configured for CA server named Contoso-CA and that is hosted on CA1 computer.

Remove-CES –Force

Will remove all CES instances from local computer. If you specify –Force switch, this will ignore CAConfig and Authentication parameters and will remove all CES instances from local computer.

Note: by default script configure IIS AppPool to ApplicationPoolIdentity. Currently I don't know how to configure AppPool to NetworkService identity, so you may have to manually switch application pool account settings after role installation. This will not required if there is at least one existing CES instance. In that case new CES instance will inherit application pool settings from existing CES instance.

If you have questions or comments — ask me :)

For script download click here:

Have a nice day! :)


Share this article:

Comments:

Ryan
Ryan 27.07.2012 07:08 (GMT+2) Add multiple Certificate Enrollment Service instances

Thank you for writing this script, I just got off with Microsoft support who told me this is not possible. I am having issues with the script it is complaining about the win32_operatingsystem call. I am running windows Server 2008 R2. Any ideas? Thank you, Ryan

Ryan
Ryan 27.07.2012 07:13 (GMT+2) Add multiple Certificate Enrollment Service instances

Thank you for writing this script, I just got off with Microsoft support who told me this is not possible. I am having issues with the script it is complaining about the win32_operatingsystem call. I am running windows Server 2008 R2. Any ideas? Thank you, Ryan

Ryan
Ryan 27.07.2012 08:45 (GMT+2) Add multiple Certificate Enrollment Service instances

up and running, please disregard my prior comment/question

Vadims Podans
Vadims Podans 27.07.2012 16:11 (GMT+2) Add multiple Certificate Enrollment Service instances

Microsoft engineer was incorrect. Windows Server 2008 R2 supports multiple instances of Certificate Enrollment Service. Do I understand that you have resolved issue?

Ryan
Ryan 28.07.2012 05:19 (GMT+2) Add multiple Certificate Enrollment Service instances

Yes, I have resolved the issue. Thank you again for creating this script it made things much easier.

Matthias
Matthias 30.03.2018 12:40 (GMT+2) Add multiple Certificate Enrollment Service instances

Hi Vadims, is it generally possible also to have multiple CEP instances on 1 server? To have one that works for Username/Password for non domain joined and one for Kerberos Authenticating Clients?

Or do you just have to change authentication settings in IIS for the CEP?

 

Thanks a lot

Vadims Podāns
Vadims Podāns 30.03.2018 17:05 (GMT+2) Add multiple Certificate Enrollment Service instances

> is it generally possible also to have multiple CEP instances on 1 server?

No, unlike CES, only single instance of CEP service can be installed on single server. This limitation came from SPN management. Each CEP instance requires separate SPN, but you can't have multiple SPNs on the same machine.


Post your comment:

Please, solve this little equation and enter result below. Captcha