Hello folks, sorry for delayed post, one of my SSD disk suddenly dead and I was busy with data recovery.

In the previous post we discovered main interfaces and methods to retrieve Online Responder array settings and revocation configurations. Today we will learn how to use them to delete existing revocation configuration and add a new one.

Deleting existing revocation configuration

In the previous post you noticed that my OCSP server has configured one revocation configuration named “test”. Consider when we don’t need this particular configuration (say, associated CA was decommissioned). We can delete it by calling IOCSPCAConfigurationCollection::DeleteCAConfiguration method and applying changes by calling IOCSPAdmin::SetConfiguration method.

PS C:\> $ocsp = New-Object -ComObject CertAdm.OCSPAdmin
PS C:\> $ocsp.GetConfiguration("dc2",$true)
PS C:\> $ocsp.OCSPCAConfigurationCollection.DeleteCAConfiguration("test")
PS C:\> $ocsp.SetConfiguration("dc2",$true)
PS C:\> $ocsp.OCSPCAConfigurationCollection
PS C:\>

There is nothing complex: we pass configuration name (which, obviously must be unique within particular OCSP array) and then call SetConfiguration by passing OCSP server name and boolean value to update information even if OcspSvc service is not running. Trust me, nothing can be simpler in PKI world!

All OCSP server and revocation configuration changes MUST BE performed on an ARRAY CONTROLLER! This is because only array controller is authorized to update configuration between array members.

Creating common revocation configuration for Enterprise CA

Here we will start with the most simple and common scenario — add new revocation configuration against an Enterprise CA. So, how to start? First, we have to identify CA server (by retrieving CA config string), then retrieve the most recent CA certificate and fetch CRL URLs to bind them to revocation provider.

There are various ways to achieve this goal. For example, ocsp.msc UI does it via ICertConfig2 and ICertAdmin2 COM interfaces:

PS C:\> # instantiate ICertConfig interface
PS C:\> $CertConfig = New-Object -ComObject CertificateAuthority.Config
PS C:\> # display Enterprise CA pick UI and save returned config string to a variable
PS C:\> $ConfigString = $CertConfig.GetConfig(0x1)
PS C:\> $ConfigString
ca01.contoso.com\Contoso SHA2 CA
PS C:\>

We got a configuration string of the CA. Now we need to get current CA certificate. In this case we will use ICertAdmin2::GetCAProperty method. ICertAdmin interface specific is outside of this article scope, so I briefly outline just required steps:

PS C:\> # connect to a CA and retrieve the count of CA certificates:
PS C:\> $CertAdmin = New-Object -ComObject CertificateAuthority.Admin
PS C:\> $CertAdmin.GetCAProperty($ConfigString,0xb,0,1,0)
1
PS C:\> # retrieve CA certificate at CA cert count minus 1:
PS C:\> $base64cert = $CertAdmin.GetCAProperty($ConfigString,0xc,0,3,1)
PS C:\> $base64cert
MIICRjCCAeugAwIBAgIQBrUmlNV5l5VCByyP7eBjnzAKBggqhkjOPQQDAjBIMRMw
EQYKCZImiZPyLGQBGRYDY29tMRcwFQYKCZImiZPyLGQBGRYHY29udG9zbzEYMBYG
A1UEAxMPQ29udG9zbyBTSEEyIENBMB4XDTEzMTEwMzEzMTMxMVoXDTMzMTEwMzEz
MjMwOVowSDETMBEGCgmSJomT8ixkARkWA2NvbTEXMBUGCgmSJomT8ixkARkWB2Nv
bnRvc28xGDAWBgNVBAMTD0NvbnRvc28gU0hBMiBDQTBZMBMGByqGSM49AgEGCCqG
SM49AwEHA0IABAWQFfIwhXcmkm1xodBb3w2+QvSLEwYJtVI9KngNeIrWzbKguCNb
abHFD5PNLk84Mev9hZJ5jy3EhDKdYZJobF2jgbYwgbMwEwYJKwYBBAGCNxQCBAYe
BABDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFIN5
aYEormhEoiCJ1bjk+DJO03NGMBAGCSsGAQQBgjcVAQQDAgEAME0GA1UdIARGMEQw
QgYEVR0gADA6MDgGCCsGAQUFBwIBFixodHRwOi8vcGtpLmJveHRvbmUuY29tL0Nv
bnRlbnRzL2Nwc21haW4uaHRtbDAKBggqhkjOPQQDAgNJADBGAiEAo51zFchZXrf3
PoRtQXYHlW5+oE0tWlI/vzKZEVriPasCIQCdzsh3PI+5YOHG9DknFHiifLLfVVkf
ySP8xLBEwWm7aw==

PS C:\> # convert base64 string to a byte array:
PS C:\> $RawData = [convert]::FromBase64String($base64cert)
PS C:\>

$RawData will contain a byte array that represents CA certificate for which revocation configuration is created. Additionally, we retrieve CRL URLs which will be used for revocation provider:

PS C:\> $CRLs = $CertAdmin.GetCAProperty($ConfigString,0x29,0,4,0)
PS C:\> $crls
ldap:///CN=Contoso%20SHA2%20CA,CN=ca01,CN=CDP,CN=Public%20Key%20Services,CN=Services,CN=Configuration,DC=contoso,DC=com
?certificateRevocationList?base?objectClass=cRLDistributionPoint

PS C:\> # remove extra trailing new line character
PS C:\> $crls = $crls.trim()

Ok, so far we retrieved almost all required information and move on, create new revocation configuration. This is achieved by calling IOCSPCAConfigurationCollection::CreateCAConfiguration method where we pass configuration name (whatever you want, I usually use CA name with CA certificate’s key index) and CA certificate (as a byte array):

PS C:\> $revconfig = $ocsp.OCSPCAConfigurationCollection.CreateCAConfiguration("Contoso SHA2 CA (0)",$RawData)
PS C:\> $revconfig


Identifier                 : Contoso SHA2 CA (0)
CACertificate              : {48, 130, 2, 70...}
HashAlgorithm              :
SigningFlags               :
SigningCertificate         :
ReminderDuration           :
ErrorCode                  : 2147483658
CSPName                    :
KeySpec                    :
ProviderCLSID              :
ProviderProperties         :
Modified                   : True
LocalRevocationInformation :
SigningCertificateTemplate :
CAConfig                   :



PS C:\>

The method returned a revocation configuration object reference. We do not store a copy of the object, but a reference to it, therefore when we modify this reference, a source object (which is stored in the $ocsp.OCSPCAConfigurationCollection property) is updated too. Current object status is 2147483658 (0x8000000a) which stands for “The data necessary to complete this operation is not yet available.” Let’s fill general properties:

PS C:\> # set hash and signing algorithm to SHA1
PS C:\> $revconfig.HashAlgorithm = "sha1"
PS C:\> # set CA which will issue OCSP Response Signing certificate:
PS C:\> $revconfig.CAConfig = $ConfigString
PS C:\> $revconfig.SigningCertificateTemplate = "OCSPResponseSigning"
PS C:\> # set provider ID. It must be "{4956d17f-88fd-4198-b287-1e6e65883b19}"
PS C:\> $revconfig.ProviderCLSID = "{4956d17f-88fd-4198-b287-1e6e65883b19}"
PS C:\> # set default signing flags:
PS C:\> $revconfig.SigningFlags = 605
PS C:\> $revconfig


Identifier                 : Contoso SHA2 CA (0)
CACertificate              : {48, 130, 2, 70...}
HashAlgorithm              : sha1
SigningFlags               : 605
SigningCertificate         :
ReminderDuration           :
ErrorCode                  : 2147483658
CSPName                    :
KeySpec                    :
ProviderCLSID              : {4956d17f-88fd-4198-b287-1e6e65883b19}
ProviderProperties         :
Modified                   : True
LocalRevocationInformation :
SigningCertificateTemplate : OCSPResponseSigning
CAConfig                   : ca01.contoso.com\Contoso SHA2 CA



PS C:\>

Here is little note for SigningFlags property. For Enterprise CAs we should use the following flag combination:

  • OCSP_SF_ALLOW_SIGNINGCERT_AUTOENROLLMENT
  • OCSP_SF_RESPONDER_ID_KEYHASH
  • OCSP_SF_AUTODISCOVER_SIGNINGCERT
  • OCSP_SF_FORCE_SIGNINGCERT_ISSUER_ISCA
  • OCSP_SF_ALLOW_SIGNINGCERT_AUTORENEWAL
  • OCSP_SF_SILENT

Combined, these flags will result a numeric value of 605.

Creating revocation provider

As it is known (or should be known), Windows OCSP relies on CRLs and we have to provide CRL location URLs to allow OCSP server to automatically download and use these CRLs. Revocation provider is implemented in a generic IOCSPPropertyCollection interface:

# instantiate and initialize IOCSPPropertyCollection interface:
$OCSPPropCollection = New-Object -ComObject CertAdm.OCSPPropertyCollection
[void]$OCSPPropCollection.InitializeFromProperties($null)
# create property that will store Base CRL URLs. URLs must be explicitly casted
# to a string array (rather than default object array)
[void]$OCSPPropCollection.CreateProperty("BaseCrlUrls",[String[]]$CRLs)
# assuming that our Enterprise CA uses Delta CRLs, generate a Delta CRL URL
$DeltaCRLURLs = $CRLs | %{
    if ($_ -match "^http") {
        $_ -replace "\.crl$", "+.crl"
    } else {
        $_ -replace "\?certificateRevocationList\?","?deltaRevocationList?"
    }
}
[void]$OCSPPropCollection.CreateProperty("DeltaCrlUrls",[String[]]$DeltaCRLURLs)
# write property collection to ProviderProperties property
$revconfig.ProviderProperties = $OCSPPropCollection.GetAllProperties()

if we run the code:

PS C:\> # instantiate and initialize IOCSPPropertyCollection interface:
PS C:\> $OCSPPropCollection = New-Object -ComObject CertAdm.OCSPPropertyCollection
PS C:\> [void]$OCSPPropCollection.InitializeFromProperties($null)
PS C:\> # create property that will store Base CRL URLs. URLs must be explicitly casted
PS C:\> # to a string array (rather than default object array)
PS C:\> [void]$OCSPPropCollection.CreateProperty("BaseCrlUrls",[String[]]$CRLs)
PS C:\> # assuming that our Enterprise CA uses Delta CRLs, generate a Delta CRL URL
PS C:\> $DeltaCRLURLs = $CRLs | %{
>>     if ($_ -match "^http") {
>>         $_ -replace "\.crl$", "+.crl"
>>     } else {
>>         $_ -replace "\?certificateRevocationList\?","?deltaRevocationList?"
>>     }
>> }
>> [void]$OCSPPropCollection.CreateProperty("DeltaCrlUrls",[String[]]$DeltaCRLURLs)
>> # write property collection to ProviderProperties property
>> $revconfig.ProviderProperties = $OCSPPropCollection.GetAllProperties()
>>
PS C:\> $revconfig


Identifier                 : Contoso SHA2 CA (0)
CACertificate              : {48, 130, 2, 70...}
HashAlgorithm              : sha1
SigningFlags               : 605
SigningCertificate         :
ReminderDuration           :
ErrorCode                  : 2147483658
CSPName                    :
KeySpec                    :
ProviderCLSID              : {4956d17f-88fd-4198-b287-1e6e65883b19}
ProviderProperties         : {BaseCrlUrls, ldap:///CN=Contoso%20SHA2%20CA,CN=ca01,CN=CDP,CN=Public%20Key%20Services,CN=
                             Services,CN=Configuration,DC=contoso,DC=com?certificateRevocationList?base?objectClass=cRL
                             DistributionPoint, DeltaCrlUrls, ldap:///CN=Contoso%20SHA2%20CA,CN=ca01,CN=CDP,CN=Public%2
                             0Key%20Services,CN=Services,CN=Configuration,DC=contoso,DC=com?deltaRevocationList?base?ob
                             jectClass=cRLDistributionPoint}
Modified                   : True
LocalRevocationInformation :
SigningCertificateTemplate : OCSPResponseSigning
CAConfig                   : ca01.contoso.com\Contoso SHA2 CA



PS C:\>

we will get the complete revocation configuration with configured revocation provider.

Current list of provider propertied is listed in the ProviderProperties MSDN article. Also, there is a typo. Plural values misses “s” character at the end: BaseCrlUrls, DeltaCrlUrls.

Now we need to commit changes to the server and check, whether it is created as expected by calling SetConfiguration method:

$OCSPAdmin.SetConfiguration("dc2",$true)

If the method succeeds, then we can open ocsp.msc to check, whether it is really ok:

image

It is up and already working!

Today we learned how simple things can be so difficult. As I always say, Windows PKI will never create easy ways to accomplish some PKI-related stuff. But if you learned this way — you are on top and rock in colleague’s eyes. In order to better understand how it works, follow this post and MSDN documentation provided in links. Spend few more minutes to arrange this mess in your heads. Eventually, you will be able to automate the whole OCSP provisioning process. For example, I wrote (for internal use) an universal script that will automatically install and configure OCSP server with parameters. Spend some time today and save more time tomorrow.


Share this article:

Comments:

Tim

Is it possible to use powershell to:
1. Set a server as the OCSP Array Controller
2. Add members to an OCSP array
3. Synchronize the OCSP array
 

Vadims Podāns

yes, it is possible to set server as the OCSP array controller and manage array membership. But there is no way to synchronize (and refresh revocation data) OCSP array from PowerShell. This functionality is implemented directly in MMC.

Tim

Thank you. These 2 OCSP articles were very helpful.

Vadims Podāns

Regarding array membership management, you can try to read my blog post in Russian: http://www.sysadmins.lv/blog-ru/upravlenie-online-responder-iz-powershell-chast-1.aspx

unfortunately, I haven't translated it to English. Try to use online translators.

cypher

I'm trying to set "RefreshTimeOut" property of $OCSPPropCollection, but it keeps throwing an error when I call $ocsp.SetConfiguration("dc",$true).  Can you tell me what I'm doing wrong?

I know that it needs a DWORD value, but I'm not sure how to properly pass that.  Here's what I've tried...

    $RefreshTimeOut = "5"
    [void]$OCSPPropCollection.CreateProperty("RefreshTimeOut",[String[]]$RefreshTimeOut)

I've also tried:

    $RefreshTimeOut = "0x5"
    [void]$OCSPPropCollection.CreateProperty("RefreshTimeOut",[String[]]$RefreshTimeOut)

And even:

    $RefreshTimeOut = 5
    [void]$OCSPPropCollection.CreateProperty("RefreshTimeOut",[Int[]]$RefreshTimeOut)

Any help would be greatly appreciated.

Vadims Podāns

The problem is that RefreshTimeOut is DWORD (simple integer), not an array of strings or array of integers. Look at this page for more details on property types: https://msdn.microsoft.com/en-us/library/aa386372(v=vs.85).aspx

The code should be:

$RefreshTimeOut = 5
[void]$OCSPPropCollection.CreateProperty("RefreshTimeOut",[int]$RefreshTimeOut)

cypher

Thanks!  I guess I didn't realize that I as trying to pass an array...

James

The DeltaCRL doesn't seem to work for multiple delta entries (we have http and LDAP). I used following instead.

$DeltaCRLURLs = $CRLs | %{

    ($_ -replace "\.crl", "+.crl") -replace "\?certificateRevocationList\?","?deltaRevocationList?"

}

James

Sorry just realised when viewiing new OCSP object that this way didnt work either, ended up with all items in one entry rather than multiple entires.

Stuart

As per comment above from James, I too have multiple entries for Base and Delta CRLs and they are ending up as a single string in the OCSP object which then fails the config when viewing in the OCSP vai the GUI. If yuo edit the configuration by hand in the GUI and make it 2 line as it shoudl be it all works.  

Has anybody found away round this?   

The comment and code in the article mentions  explicityly casting to a string array (rather than the default object array). could it be linked this casting?

              # create property that will store Base CRL URLs. URLs must be explicitly casted
              # to a string array (rather than default object array)
              [void]$OCSPPropCollection.CreateProperty("BaseCrlUrls",[String[]]$CRLs)

 

Any help appreciated?

Dmitry

Hi. I had installed OCSP resronder on Enterprise sub-ca

All work correct, but when i trying to check certs from F5 - i have error "no responder" 

From windows os check by cert util work correct. 

 

Vadims Podāns

> when i trying to check certs from F5 - i have error "no responder"

You need to consult with F5 documentation or contact F5 support.

p.s. it is not recommended to instal OCSP on CA server.


Post your comment:

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