if (ms) blog++;

Random bits of (hopefully) useful technical information on Windows, with a focus on understanding and troubleshooting.

Q: When is a validity period not a validity period?

Q: When is a validity period not a validity period?

  • Comments 3
  • Likes

A: When it’s a Certificate Authority after Windows 2000.

 

Back Story…

Those helpful chaps over at the Windows PKI Blog provided a script (setupca.vbs) to install a Certificate Authority on Windows Server 2008 and R2, and recently we had a request to be able to specify the expiration date of the CA’s own certificate (the default is 5 years).

In deconstructing the script to add this functionality I ran into a couple of issues which I thought it might be useful to share here…

From MSDN I found the ICertSrvSetup Interface which points to the method SetCASetupProperty, which is used to update a CASetupProperty.

So far, so good – the CASetupProperty typedef shows 2 properties of interest; ENUM_SETUPPROP_VALIDITYPERIOD and ENUM_SETUPPROP_VALIDITYPERIODUNIT.

 

First “gotcha” – the descriptions posted on MSDN for these 2 properties are backwards:

ENUM_SETUPPROP_VALIDITYPERIOD
A VT_I4 value that specifies the number of units in the validity period as specified by the ENUM_SETUPPROP_VALIDITYPERIODUNIT property type. For a subordinate CA, the validity period is determined by the parent CA.

ENUM_SETUPPROP_VALIDITYPERIODUNIT
A VT_I4 value that specifies a value of the ENUM_PERIOD enumeration that indicates the time units of the validity period. For a subordinate CA, the validity period time unit is determined by the parent CA.

There is a reason for this – in Windows Server 2000 this is how it was, but from Windows Server 2003 onwards these got switched around.


If you check out the Ask the Directory Services Team blog you can see the definitions listed down in the CAPolicy.inf section where they talk about the history of RenewalValidityPeriod and RenewalValidityPeriodUnits along the same vein.

 

The second “gotcha” was due to VBScript not being strongly-typed – when trying to call SetCASetupProperty to specify SETUPPROP_VALIDITYPERIODUNIT was equal to 13 (as a test), the script engine kept falling over with error 0x80071707:
”The actual data type of the property did not match the expected data type of the property”

It seems my 13 wasn’t big enough :)

The original code looked like this:
Call g_oCASetup.SetCASetupProperty(SETUPPROP_VALIDITYPERIODUNIT, iSelectedValidYears)

To make it work, I needed to type-cast the variable to the expected VT_I4 type as defined by MSDN, using CLng():
Call g_oCASetup.SetCASetupProperty(SETUPPROP_VALIDITYPERIODUNIT, CLng(iSelectedValidYears))

 

With those wrinkles ironed out, the script ran happily with my new “/SV” switch to specify the validity period in years – the screenshot below shows the debug spew confirming that I was updating SETUPPROP_VALIDITYPERIODUNIT, and that the certificate produced was indeed valid for 13 years from the current date, using an 8K key length:

SetupCA-Revisited

 

To avoid multiple copies of the same script floating around, requiring parallel updates if further changes are made in the future, I won’t post the whole script here, but I will document the bits I added so you can implement them in the setupca.vbs yourself if needed.

For each of the changes below, the lines in blue are from the original script, and the lines in red are the bits I inserted:

1. Add the documentation for the new switch:

    Call OutputLine(ECHOMINIMAL, "/SA <Alg>    - Specify Hash algorithm")
    Call OutputLine(ECHOMINIMAL, "/SV <Validity>    - Specify Validity period (in years)")
    Call OutputLine(ECHOMINIMAL, "/SN <Name>   - Specify CA Name")

 2. Add a constant to define SETUPPROP_VALIDITYPERIOD as years (for simplicity we won’t make this a variable):

Const SETUPPROP_WEBCANAME = 16

'CA validity period is an ENUM_PERIOD type, here we only allow YEARS
Const ENUM_PERIOD_YEARS = 6

3. Add a variable to hold the (optional) validity period specified at the command line:

'Signing key length
Dim iSelectedKeySize
iSelectedKeySize = "" ' DEF_SEL_KEY_SIZE

'Validity period for the CA certificate, if specified by the user
Dim iSelectedValidYears
iSelectedValidYears = ""

'Save request to file, for submitting to offline root
Dim strRequestFile
strRequestFile = ""

 4. Add the debug output to confirm the specified validity period (if it was entered):

    wscript.echo "bWebPages: " & bWebPages
    wscript.echo "iSelectedKeySize: " & iSelectedKeySize

    wscript.echo "iSelectedValidYears: " & iSelectedValidYears

 5. Add the case to parse the command line switch “/SV”:

            Case "/sk"

                If Not blnGetArg("Key length", iSelectedKeySize, intArgIter) Then
                    intParseCmdLine = CONST_ERROR
                    Exit Function ' intParseCmdLine
                End If

                intArgIter = intArgIter + 1

            Case "/sv"

                If Not blnGetArg("Validity Period", iSelectedValidYears, intArgIter) Then
                    intParseCmdLine = CONST_ERROR
                    Exit Function ' intParseCmdLine
                End If

                intArgIter = intArgIter + 1

            Case "/sa"

                If Not blnGetArg("Hash algorithm",strSelectedHashAlg, intArgIter) Then
                    intParseCmdLine = CONST_ERROR
                    Exit Function ' intParseCmdLine
                End If

                intArgIter = intArgIter + 1

 6. Add the code to set the object properties SETUPPROP_VALIDITYPERIOD and SETUPPROP_VALIDITYPERIODUNIT:

  If (bReuseCert = False) Then
   Dim bProviderSet
   bProviderSet = SetProvider(g_oCASetup, strSelectedCSP, strSelectedHashAlg, iSelectedKeySize)

   If (False = bProviderSet) Then
    Call OutputLine(ECHOMINIMAL, "InstallAndVerifyCA:unable to set key properties!")
    Exit Function 'InstallAndVerifyCA
   End If 'error occurred

   If (iSelectedValidYears <> "") Then
    'Specify CA validity period (NOTE: this is the unit SIZE!)
    Call g_oCASetup.SetCASetupProperty(SETUPPROP_VALIDITYPERIOD, CLng(ENUM_PERIOD_YEARS))

    If (0 <> Err.Number) Then
     Call PrintErrorInfo("InstallAndVerifyCA:unable to set SETUPPROP_VALIDITYPERIOD!", Err)
     Exit Function 'InstallAndVerifyCA
    End If
    Call Err.Clear()

    'Specify CA validity period unit (NOTE: this is the NUMBER of units!)
    Call OutputLine(ECHOMINIMAL, "InstallAndVerifyCA: SetCASetupProperty - ValidityPeriodUnit = " & CLng(iSelectedValidYears))
    Call g_oCASetup.SetCASetupProperty(SETUPPROP_VALIDITYPERIODUNIT, CLng(iSelectedValidYears))

    If (0 <> Err.Number) Then
     Call PrintErrorInfo("InstallAndVerifyCA:unable to set SETUPPROP_VALIDITYPERIODUNIT!", Err)
     Exit Function 'InstallAndVerifyCA
    End If
    Call Err.Clear()
   End If
  End If

 

The code above is very, very briefly tested on my W2K8R2 lab server and is provided “as-is”, without any liability or support.

There is no error or boundary checking done on the value entered.

Be careful of any inadvertent line-wrapping that may occur when viewed in small browser windows, VBScript is very unforgiving…

Comments
  • Thanks for you input on this script.   Very helpful.

  • Great modification !!!!!!!! Thanks a lot for this.

  • Thank you for the script modification!  Having the ability to specify the validity period of the CA's own certificate was tremendously helpful as I am deploying in a server core environment.

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment