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:
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.
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! :)
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
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
up and running, please disregard my prior comment/question
Microsoft engineer was incorrect. Windows Server 2008 R2 supports multiple instances of Certificate Enrollment Service. Do I understand that you have resolved issue?
Yes, I have resolved the issue. Thank you again for creating this script it made things much easier.
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
> 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.
Hello Vadims - We configured our CES/CEP server to use username/password authentication and it works great. Is it possible to change the authentication type after the fact to use certificate authentication rather than username/password w/out removing and reinstalling the role?
Thanks,
Víctor
> Is it possible to change the authentication type after the fact to use certificate authentication rather than username/password w/out removing and reinstalling the role?
no, there is no other way. You have to uninstall existing service and install a new one.
Hello Vadims, here is a doc from Microsoft where it says that "Two CEP/CES instances that are configured on one server" : https://docs.microsoft.com/en-us/windows-server/identity/solution-guides/certificate-enrollment-certificate-key-based-renewal
As far as I tested it, these powershell commands used to configure the second instances of CEP/CES don't work for me (for now) : https://docs.microsoft.com/en-us/windows-server/identity/solution-guides/certificate-enrollment-certificate-key-based-renewal#step-1-install-the-cep-and-ces-for-key-based-renewal-on-the-same-server
Here the error I get :
PS ...> Install-AdcsEnrollmentPolicyWebService -AuthenticationType Certificate -SSLCertThumbprint "xxxxxxxxxxxxxxxxxxxxxxxxx"
...
Performing the operation "Install-AdcsEnrollmentPolicyWebService" on target "XXXXXXXX".
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "Y"): Y
Install-AdcsEnrollmentPolicyWebService : Setup could not add this role service because it already exists in the default Web site. Please remove the existing role
service or select a different certification authority (CA) or authentication type. Cannot create a file when that file already exists. 0x800700b7 (WIN32/HTTP: 183
ERROR_ALREADY_EXISTS)
At line:1 char:1
+ Install-AdcsEnrollmentPolicyWebService -AuthenticationType Certificat ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [Install-AdcsEnrollmentPolicyWebService], EnrollmentPolicyServiceSetupException
+ FullyQualifiedErrorId : Install,Microsoft.CertificateServices.Deployment.Commands.CEP.InstallAdcsEnrollmentPolicyWebService
Could you please tell me what is your opinion about this doc ?
Thank you in advance !
For your information :
PS ...> certutil -config "<myCA>" -enrollmentserverurl
Enrollment Server Url[0]:
Priority 1
Authentication 4
UserName -- 4
AllowRenewalsOnly 0
https://myServer/...-CA_CES_UsernamePassword/service.svc/CES
AllowKeyBasedRenewal 0
CertUtil: -enrollmentServerURL command completed successfully.
It is correct: you cannot have multiple CEP (policy servers) instances on same server. Only multiple enrollment services (CES) are supported. And this blog post talks about CES, not CEP.
Thank Vadims for your quick answer!
Perhaps I didn't fully understand this article but there are screenshots about IIS console with two CEP Applications ("ADPolicyProvider_CEP_UsernamePassword" and "ADPolicyProvider_CEP_Certificate") for example this one.
Hello,
Finally I managed to do it after reading this Ms doc : https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-server-2012-R2-and-2012/jj590165(v=ws.11)?redirectedfrom=MSDN#to-install-a-certificate-enrollment-policy-web-service-that-uses-certificate-authentication
Before using this command :
> Install-AdcsEnrollmentPolicyWebService -AuthenticationType Certificate -SSLCertThumbprint "xxxxxxxxxxxxxxxxxxxxxxxxx"
Execute this command:
> cd cert:\LocalMachine\My
Hi Vadims,
I believe you can install multiple CEP instances on the same server. As François mentioned above, he seems to find the command to run before adding the 2nd CEP auth.
My situation is that I am trying to use a MSA instead of regular domain user account. It worked with Kerberos and Username/Password options, but not with Certificate /KeyBasedRenewal. And I am trying to figure out why.
You don't need to run CEP service under MSA or domain user account. You can use built-in app pool account for that. Where you really want to change domain account with MSA is CES service. Though, I never was able to do it. Impersonation step always fail to me even if SPNs are set correctly.
The trick above is very interesting, though, I never tried to install more than one CEP role on single server. I did it for CES, bet never for CEP.
Hello Vadims,
First of all, I like to say thank you very much for all your wonderful posts! They helped me a lot in the past three months on setting up our new PKI.
But now I struggle with this CES and CEP deployment with Kerberos Authentication. So it is very interesting to read this.
I have found out that I did need to enable Kernel-Mode Authentication in IIS on both CES and CEP to make it work. To make it work on a server where the CA is placed. But I am unsure if this is considered secure. Do you have any thoughts on this?
What I have done is:
* Set SPNs for each CEP and CES instance on their own via a CNAME (found this hint after Server Manager got broken)
* Created a service user -> Like to test it later with this MSU
* Added user to IIS_USR Group on both machines
* Set delegation from the service user to the CA server
* Enabled this IIS Kernel-mode authentication
* Setup GPO to enable the service user to "Allow log on locally", "Impersonate a client after authentication", "Log on as batch job", "Log on as service" and to make everything sure "Obtain an impersonation token for another user in the same session"
But I face an issue if I place CES on another server. I get always thrown Reposnse Codes 500. Not sure how to track it down, and find better logs than those IIS "Failed Request Tracing Rules". Any hint you can give me to look further where this Authentication Error comes from?
Thank you very much!
Hello Vadims,
I finally solved all of my issues! Your website still helped a lot. So Thank you very much again for all this excellent content.
I can tell you that I have now running CEP and CES. Both with Kerberos Constrained Delegation on the same server, including a gMSA.
Some things I figured out over the time:
Then all this is working out of the box. No further configuration for CEP is needed. It can run under the default Application Pool. Even more without additional rights, it will even fail.
I have a link in which some PowerShell commands describe how this works and which did give me personally the final understanding. Not sure if I am allowed to post it here.
If you like to get to know about my configuration feel free to contact me.
I hope this post might help others too.
Kind regards
Felix Wagner
After installing CEPCES roles for 1st-time enrollment only, it shows the CES URI but not if this has impacted my Manually SSL cert enrollment.
Does it impact any already existing ADCS enrollment policy or it can add one new Enrollment policy for CES and would work without interrrupting existing Enrollment policy
Once installed, CES endpoint will be automatically added to Active Directory and discoverable by CEP and clients. It doesn't overwrite existing CES endpoints
Can i use this script on windows server 2019 datacenter?
On Windows Server 2019 you can use built-in Install-AdcsEnrollmentWebService command from AdcsDeployment module.
hi got an issue using this it says
PS C:\Windows\system32> Install-AdcsEnrollmentWebService -applicationpoolidentity -CAConfig "<removed ca name>" -AuthenticationType username
Install-AdcsEnrollmentWebService : You cannot set this property because the application pool "WSEnrollmentServer"
already exists. The group or resource is not in the correct state to perform the requested operation. 0x8007139f
(WIN32: 5023 ERROR_INVALID_STATE)
At line:1 char:1
+ Install-AdcsEnrollmentWebService -applicationpoolidentity -CAConfig " ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Install-AdcsEnrollmentWebService], EnrollmentServiceSetupException
+ FullyQualifiedErrorId : SetCESProperties,Microsoft.CertificateServices.Deployment.Commands.CES.InstallAdcsEnroll
mentWebService
Post your comment:
Comments: