12

Right now, I'm doing the following to request a cert from a CEP server:

  • Open gpedit.msc
  • Under Computer Configuration > Windows Settings > Security Settings > Public Key Policies, double click "Certificate Services Client - Certificate Enrollment Policy"
  • Enable
  • Enter the CEP URI
  • Switch to Username/Password authentication
  • Validate (Provide Creds)
  • Open MMC, and import Certificates snap in
  • Go to Certificates > Personal
  • Right-Click > Request New Certificate
  • Enter "more information" (CN, DNS Name, etc.)
  • Provide Creds

After this I have a cert from the CEP; however, this is a painful process to do manually. Is there any way to automate this in Server 2008 (and 2012)? All information that I can find about this tells how to install the CEP services to make a server an enrollment policy server (nothing about actually requesting a new cert, or enabling it on the client side). Is it possible to automate this?

It looks like this process adds a lot of data under HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Cryptography. Can I manually add this (and spoof a GUID/ServiceID)?

EGr
  • 609
  • 4
  • 14
  • 29
  • 1
    Not to discourage your enthusiasm, as I also like to POWERSHELL ALL THE THINGS!!!!!! but the group policy cmdlets are still absolutely horrid, and the PKI cmdlets are much improved in v4 over v3, so... :( However, [I do find this 3rd party PKI module for PowerShell module](https://pspki.codeplex.com/wikipage?title=Revoke-Certificate) useful, and it may take some of the manual pain out what you're trying to do. – HopelessN00b Dec 16 '14 at 01:29

4 Answers4

5

This is the process I have used on Windows 2012 R2 and higher. All PowerShell code was run from an elevated PowerShell prompt. Complete automation is left as an exercise for the user.

Prerequisite

Ensure that you have a Template on your Certificate Server that has the "Supply in the request" radio button selected in the Subject tab. Because this is not an AD machine, the certificate server cannot adequately query Active Directory for the information.

Export the Root

Export the Trusted Root Certification Authority Certificate on your Certificate Server and then copy that certificate file to your Target Server

certutil --% -ca.cert <name of certificate file>

Trust the Root

Import that certificate to the Trusted Root Certification Authority on your Target Server

$PathToCertificate=<name of certificate file>
$RootCertificate=Get-PfxCertificate -FilePath $PathToCertificate
$AlreadyExists=Get-ChildItem -Path "Cert:\LocalMachine\Root" | Where-Object { $_.Thumbprint -eq $RootCertificate.Thumbprint }
if ($AlreadyExists -eq $null) { Import-Certificate -CertStoreLocation "Cert:\LocalMachine\Root" -FilePath $PathToCertificate }
else { Write-Warning "Root certificate already installed" }

Active Directory Policy Provider

Determine the URL for the Active Directory Policy Provider

# Get AD Configuration Context
$RootDSE=[System.DirectoryServices.DirectoryEntry]::new("LDAP://RootDSE")
$ConfigContext="CN=Enrollment Services,CN=Public Key Services,CN=Services,"+$RootDSE.configurationNamingContext
# Get name of Enterprise Root Certificate Autority server
$Server=Get-ADObject -SearchBase $ConfigContext -Filter "*"
if ($Server.Count -eq 1) { throw "No Enterprise Root Certificate Autority server exists" }
else { $Server=$Server[1].Name }
# Get Enrollment URL
$ConfigContext="CN=$Server,"+$ConfigContext
$EnrollmentURL=(Get-ADObject -SearchBase $ConfigContext -Filter "*" -Properties "msPKI-Enrollment-Servers" | Select-Object -ExpandProperty "msPKI-Enrollment-Servers").Split("`n") | Where-Object { $_ -like "http*" }
if ($EnrollmentURL -eq $null) { $EnrollmentURL="" }
# Get AD Enrollment Policy URL
$Server=$Server+$RootDSE.configurationNamingContext.Value.Replace("CN=Configuration","").Replace(",DC=",".")
$WMI=Get-WmiObject -ComputerName $Server -Namespace "root\WebAdministration" -Class Application | Where-Object { $_.Path -eq "/ADPolicyProvider_CEP_UsernamePassword" }
if ($WMI -ne $null) { $PolicyURL="https://"+$Server+$WMI.Path+"/service.svc/CEP" }
else { $PolicyURL="" }
Write-Output "Enrollment URL = $EnrollmentURL"
Write-Output "Policy URL = $PolicyURL"

Enrollment Policy

Add the Enrollment Policy to the Target Server (This only works on Windows 2012 and higher. For GUI instructions, see below). Make sure that the Policy is added after you create the non-domain template, otherwise it will not appear as it the policy does not get refreshed.

$User="<your domain name>\<your domain user name>"
$Pass="<Your domain password>"
$SecPass=ConvertTo-SecureString -String $Pass -AsPlainText -Force
$Cred=[System.Management.Automation.PSCredential]::new($User,$SecPass)
Add-CertificateEnrollmentPolicyServer -Url $PolicyURL -context Machine -NoClobber -AutoEnrollmentEnabled -Credential $Cred

Get the certificate

You should now be able to enroll for a certificate using the desired template

$DNS="<FQDN of your server>"
$URL=Get-CertificateEnrollmentPolicyServer -Scope All -Context Machine | Select-Object -ExpandProperty Url | Select-Object -ExpandProperty AbsoluteUri
$Enrollment=Get-Certificate -Url $URL -Template "<Template name (not display name)>" -SubjectName "CN=$DNS" -DnsName $DNS -Credential $Cred -CertStoreLocation cert:\LocalMachine\My
$Enrollment.Certificate.FriendlyName=$DNS

Enrollment Policy Pre-Windows 2012 R2

  1. Open the certificates MMC
  2. Drill down to the Personal Certificate Store
  3. Right click "Certificates" and selet All Tasks/Advanced Operations/Manage
  4. Enrollment Policies from the context menu
  5. Click the "Add" button
  6. Paste the URL for the enrollment policy
  7. Select Username/password as the Authentication type
  8. Click the "Validate Server" button and enter your domain credentials, including the domain
  9. Assuming you have been able to successfully validate, click the "Add" button
  10. Select the enrollment policy and click the "Properties" button
  11. Make sure that the "Enable for automatic enrollment and renewal" box is checked
  12. I have never checked the "Require strong validation during enrollment" so I don't know what it does
  • For use with the default LDAP method, the only thing necessary was `Get-Certificate` with a Template name. If you don't supply the URL, the default is used automatically. – DustWolf Dec 22 '22 at 15:57
  • In our AD, I've found an object of class `pkiEnrollmentService` in the corresponding container, but there is no `msPKI-Enrollment-Servers` attribute in it. Rights in AD are on per object basis, not on per attribute, right? Just a data point. – Seva Alekseyev Feb 24 '23 at 20:07
3

I presume your certificate requests are made using a template. If that's the case then use the Public Key Policies/Certificate Services Client - Auto-Enrollment Settings GPO to enforce auto enrollment. You'll also want to ensure the template ACL has Enroll and AutoEnroll marked for either domain computers or domain users (or whatever acl object, depending on the intended audience) There's a user config and computer config policy to leverage depending on whether or not it's a machine cert or user cert you're trying to push. Enrollment begins as soon as when the policy is pushed (usually about 15 minutes) after the GPO is linked and enforced.

Colyn1337
  • 2,397
  • 2
  • 23
  • 40
  • 2
    Well, duh. I got so hung up on the PowerShell part, I totally blanked on the fact that I've set up many, many GPOs to automate certificate enrollment for many years. D'oh! Good catch. – HopelessN00b Dec 17 '14 at 22:40
  • This is definitely an option (probably the best option), but I would prefer to set a local group policy setting.. is this possible? – EGr Dec 18 '14 at 20:35
  • 2
    @EGr why in the *world* would you want to do that? – MDMoore313 Dec 18 '14 at 20:50
  • @HopelessN00b I did the same thing, however it would be cool to have a powershell way to do it for troubleshooting purposes at the very least, so it's still a pretty good question. – MDMoore313 Dec 18 '14 at 20:51
0

Powershell's Get-Certificate has some limitations. In my case, it doesn't support enrolling with computer credentials. The CertEnroll COM library, however, does. Here is a Powershell example. The template name is hard-coded; there is API in CertEnroll for dynamic CA/template discovery (via Active Directory and LDAP), but I didn't bother. The template that we have expects the users to provide the subject and the SAN extension, and so this snippet does.

# Parameters
$ServerName = "myserver.example.com"
$TemplateName = "MyTemplate"

$ce = New-Object -COM X509Enrollment.CX509Enrollment
$ce.InitializeFromTemplateName(3 <#ContextAdministratorForceMachine#>, $TemplateName)
$ce.Silent = $true
# Quirk: the real PKCS10 cert request is embedded inside a CMC one
$req = $ce.Request.GetInnerRequest(0 <#LevelInnermost#>)

# Set Subject to the server's name
# Feel free to use a more involved DN
$subj = New-Object -COM X509Enrollment.CX500DistinguishedName
$subj.Encode("CN="+$ServerName, 0 <#XCN_CERT_NAME_STR_NONE#>)
$req.Subject = $subj

# Set Alt Name to the server's name - feel free to provide more names
# In our template, that extension is absent. We don't check for existence.
$altname = New-Object -COM X509Enrollment.CAlternativeName
$altname.InitializeFromString(3 <#XCN_CERT_ALT_NAME_DNS_NAME#>, $ServerName)
$altnames = New-Object -COM X509Enrollment.CAlternativeNames
$altnames.Add($altname)
$ext = New-Object -COM X509Enrollment.CX509ExtensionAlternativeNames
$ext.InitializeEncode($altnames)
$req.X509Extensions.Add($ext)

$ce.Enroll() # It may exception or it may report an error via Status
$st = $ce.Status.Status
# 1 means success

In the case of our IT shop, I don't control the template and the associated CA. Other settings on the issuing side might need different request details.

The library docs don't mention that that, but pretty much every interface there has a corresponding coclass. The ProgID convention is - take the interface name, replace the initial "I" with a "C", and prefix with "X509Enrollment.". So for a CertEnroll interface called IFoo, the ProgID would be X509Enrollment.CFoo.

Seva Alekseyev
  • 197
  • 2
  • 12
0

I don't have a complete solution, however, I can advice start points. My PowerShell PKI module has an ability to register enrollment service endpoint starting with Windows 7/Windows Server 2008 R2 (note that Windows Server 2008 do not support enrollment services). Here is an example how to register a policy: http://en-us.sysadmins.lv/Lists/Posts/Post.aspx?ID=101

regarding enrollment. This blog post series may give you some insight about how to utilize CertEnroll COM interfaces to perform certificate enrollment in PowerShell. There is nothing about enrollment web services (unfortunately), but the techniques are the same. You will need to start with this interface: IX509CertificateRequestPkcs10V2

HTH

Crypt32
  • 6,639
  • 1
  • 15
  • 33