Thursday 7 June 2012

Renew OCSP signing certificate

In my previous post, I described on how to automate the creation of an ocsp responder configuration. This post describes on how to renew and replace the signing certificate when it is about to expire. It also replaces the signing certificate for all ocsp responder configurations. This script automates the process, but there is still some interaction required. The user that runs the script needs to click OK a few times.

Required parameters:

  • $servername: The server name (eg. OcspServer)
  • $signingcertificate: The DN of your signing certificate (eg. CN=ocspSigning, OU=Cert, O=Company, C=Country)

The two parameters can be passed on the command line, hardcoded or saved in an xml file.

The first thing we need is the serial of the OCSP signing certificate that is about to expire. We get it out of the local certificate store and store the serial number in a variable.
# Save the old ocsp signing certificate in a variable
$SigningCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate
$SigningCert = ls cert:\LocalMachine\My | where {$_.Subject -eq $signingcertificate}

# Get the serial number of the old ocsp signing certificate
$serial = $SigningCert.SerialNumber
We then use certutil to get the key container. This part is a little tricky since this value is stored among a lot of other information. We pull the information we need by string matching and manipulation.
# Get the keycontainer id of the old ocsp signing certificate
$key = certutil.exe -store -v My $serial | Select-String -pattern "Key Container"
$keyContainer = ($key.ToString() -replace "Key Container = ","").Trim()
Once we have the key container value, we can write a certificate request file 'ocspsigigning.inf' and pass it to the certreq command. We store the result in the file 'ocspsigning.req'.
# Write the certificate request info to a file
Write-Output "[NewRequest]" | Out-File "ocspsigning.inf"
Write-Output "RenewalCert = $serial" | Out-File "ocspsigning.inf" -append
Write-Output "KeyContainer = $keyContainer" | Out-File "ocspsigning.inf" -append
Write-Output "UseExistingKeySet = True" | Out-File "ocspsigning.inf" -append
Write-Output "MachineKeySet = True" | Out-File "ocspsigning.inf" -append

# Request a renewal of the ocsp signing certificate using the constructed info
Certreq -new ocspsigning.inf ocspsigning.req
We can now submit the request. We save the returned information because we need the requestID to retrieve the issued certificate. As you have seen above, the same pattern matching and string manipulation is used to obtain this value. Finally we accept the certificate.
# Save the request id in a variable while submitting the request
$reqId = Certreq -submit ocspsigning.req ocspsigning.cer | Select-String -pattern "RequestId: [0-9]"
$reqId = ($reqId.ToString() -replace "RequestId:","").Trim()

# Retrieve the requested certificate
Certreq -retrieve $reqId ocspsigning.cer

# Store the certificate in the local certificate store
Certreq -accept -machine ocspsigning.cer
Now that we have a renewed signing certificate, we can use it to sign the ocsp responses. Therefore, we need to reassign the renewed certificate to all ocsp responder configurations. We save the raw certificate data of the new certificate and iterate over all OCSP configurations in the server array.
# Save the new ocsp signing certificate in a variable
$SigningCert = ls cert:\LocalMachine\My | where {$_.Subject -eq $signingcertificate}
$SigningCert = $SigningCert.GetRawCertData()

# Assign the new ocsp signing certificate to all revocation configurations
$OcspAdmin = New-Object -com "CertAdm.OCSPAdmin"
$OcspAdmin.GetConfiguration($servername, $true)
$OcspAdmin.OCSPCAConfigurationCollection | ForEach-Object {
  Write-Host "Replacing signing certificate for" $_.Identifier
  $_.SigningCertificate = $SigningCert
 }

# Commit the new configuration
$OcspAdmin.SetConfiguration($servername, $true)

Following links were very helpful when I was writing this script:

OCSP powershell script

I read following question on http://blogs.technet.com/b/askds/.

Question

Are there any available PowerShell, WMI, or command-line options for configuring an OCSP responder? I know that I can install the feature with the Add-WindowsFeature, but I'd like to script configuring the responder and creating the array.

Answer

[Courtesy of the Jonathan “oh no, feet!” Stephens – Ned]

There are currently no command line tools or dedicated PowerShell cmdlets available to perform management tasks on the Online Responder. You can, however, use the COM interfaces IOCSPAdmin and IOSCPCAConfiguration to manage the revocation providers on the Online Responder.

  1. Create an IOSCPAdmin object.
  2. The IOSCPAdmin::OCSPCAConfigurationCollection property will return an IOCSPCAConfigurationCollection object.
  3. Use IOCSPCAConfigurationCollection::CreateCAConfiguration to create a new revocation provider.
  4. Make sure you call IOCSPAdmin::SetConfiguration when finished so the online responder gets updated with the new revocation configuration.

Because these are COM interfaces, you can call them from VBScript or PowerShell, so you have great flexibility in how you write your script.

My solution

I have written and tested a powershell script that creates an ocsp configuration.

Required parameters:

  • $cert: CA certificate (stored locally)
  • $crl: URL of the corresponding crl
  • $servername: The server name (eg. OcspServer)
  • $signingcertificate: The DN of your signing certificate (eg. CN=ocspSigning, OU=Cert, O=Company, C=Country)

The first two parameters can be passed on the command line, the other two can be hardcoded or saved in an xml file.

First thing we do is save the raw certifcate data of the signing certificate.

# Create a new certificate object
$SigningCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate

# Get the certificate from the local store by using it's DN
$SigningCert = ls cert:\LocalMachine\My | where {$_.Subject -eq $signingcertificate}

# Save the raw certificate data
$SigningCert = $SigningCert.GetRawCertData()
Next we convert the string that points to the certificate to a file, take it's location and save the raw certificate data of the ca certificate.
$file = Get-Childitem $cert
$certName = $file.Name
$certPath = $file.DirectoryName

$CaCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate
$CaCert.Import($certPath + "\" + $certName)
$CaCert = $CaCert.GetRawCertData()
We then create an OCSPPropertyCollection. In here we store the url of the certificate revocation list and we specify the refresh interval. After the time specified in the refresh interval, windows will download the newest version of the crl from the specified location.
# Save the desired OcspProperties in a collection object
$OcspProperties = New-Object -com "CertAdm.OCSPPropertyCollection"
$OcspProperties.CreateProperty("BaseCrlUrls", $crl)
$OcspProperties.CreateProperty("RevocationErrorCode", 0)

# Sets the refresh interval to 1 hour (time is specified in milliseconds)
$OcspProperties.CreateProperty("RefreshTimeOut", 3600000)
Finally, we create an OCSPAdmin object and create a new revocation provider.
# Save the baseName in a variable, this is the filename without extension
# eg. basename of certificate.cer is certificate
$certBaseName = $file.BaseName

# Save the current configuration in an OcspAdmin object
$OcspAdmin = New-Object -com "CertAdm.OCSPAdmin"
$OcspAdmin.GetConfiguration($servername, $true)

# Create a new revocation configuration
$NewConfig = $OcspAdmin.OCSPCAConfigurationCollection.CreateCAConfiguration($certBaseName, $CaCert)
$NewConfig.HashAlgorithm = "SHA1"
$NewConfig.SigningFlags = 0x020
$NewConfig.SigningCertificate = $SigningCert
$NewConfig.ProviderProperties = $OcspProperties.GetAllProperties()
$NewConfig.ProviderCLSID = "{4956d17f-88fd-4198-b287-1e6e65883b19}"
$NewConfig.ReminderDuration = 90

# Commit the new configuration to the server
$OcspAdmin.SetConfiguration($servername, $true)

Following links were very helpful when I was writing this script: