Appendix 2: Sample Scripts

Applies To: Windows Server 2003 with SP1

This section contains two sample Visual Basic scripts that may be used to simplify or customize the various processes described in previous sections.

Reqdccert.vbs – Generates Domain Controller Certificate Requests

The reqdccert.vbs script makes it easier to create the correct INF file that is required to submit a certificate request to a Windows 2000 or Windows Server 2003 CA. Since it is fairly complicated to determine the GUID of a computer and create an ASN.1 file that contains both the GUID and the DNS name, the script generates the ASN.1 file automatically. In addition, a batch file is created that contains the certreq –submit command(s), which are required to submit the certificate request to the CA. Finally, a validation script is generated that allows you to verify the certificate(s) on a domain controller’s computer object after you have published the domain controller certificate(s) in Active Directory.

If you plan to submit a certificate request to a Windows 2000 or a Windows Server 2003 stand-alone CA, run the script without any command-line parameters. If you plan to submit the certificate request to a Windows Server 2003 enterprise CA, you must specify the name of the certificate template that will be used to enroll the domain controller certificate.

Note

Since the script requires the Windows Server 2003 version of certutil for an internal encoding operation, it is highly recommended, on a Windows 2000 domain controller, to put this script in the same directory as the Windows Server 2003 version of certutil and certreq.exe.

Set oArgs = WScript.Arguments 
Set oShell = WScript.CreateObject("WScript.Shell") 
' 
' Parse command line 
' 
if oArgs.Count < 1 then 
    sTemplateName = "DomainController" 
    sType = "E" 
else 
    if ((oArgs(0) = "-?") or (oARgs.Count < 2)) then 
        Wscript.Echo "Usage: reqdccert.vbs [Templatename] [Type]" 
        Wscript.Echo "[Templatename] is the name of a V2 template" 
        Wscript.Echo "[Type]         can be E for Email and A for Authentication certificate" 
        Wscript.Echo "If no option is specified, the DomainController certificate template is used." 
        Wscript.Quit 1 
    else 
        sTemplateName = oArgs(0) 
                sType = oArgs(1) 
    end if 
end if 
Set oFilesystem = CreateObject("Scripting.FileSystemObject") 
Set objSysInfo = CreateObject("ADSystemInfo") 
Set objDC = GetObject("LDAP://" & objSysInfo.ComputerName) 
sGUID = objDC.GUID 
sDNShostname = objDC.DNShostname 
sHostname = objDC.cn 
'############################################################################## 
' 
' Create the ASN.1 file 
' 
'############################################################################## 
Dim aASNsubstring(2, 5) 
Const HEX_DATA_LENGTH = 1 
Const ASCIIDATA = 2 
Const HEXDATA = 3 
Const HEX_BLOB_LENGTH = 4 
Const HEX_TYPE = 5 
aASNsubstring(0, ASCIIDATA) = sDNShostname 
aASNsubstring(0, HEX_TYPE) = "82" 
' 
' Convert DNS name into Hexadecimal 
' 
For i = 1 to Len(aASNsubstring(0, ASCIIDATA)) 
    aASNsubstring(0, HEXDATA) = aASNsubstring(0, HEXDATA) & _ 
                                    Hex(Asc(Mid(aASNsubstring(0, ASCIIDATA), i, 1))) 
Next 
aASNsubstring(0, HEX_DATA_LENGTH) = ComputeASN1 (Len(aASNsubstring(0, HEXDATA)) / 2) 
' 
' Build the ASN.1 blob for DNS name 
' 
sASN = aASNsubstring(0, HEX_TYPE) & _ 
       aASNsubstring(0, HEX_DATA_LENGTH) & _ 
       aASNsubstring(0, HEXDATA) 
' 
' Append the GUID as other name 
' 
if (sType = "E") then 
    aASNsubstring(1, HEXDATA) = sGUID 
    aASNsubstring(1, HEX_TYPE) = "A0" 
    aASNsubstring(1, HEX_DATA_LENGTH) = ComputeASN1 (Len(aASNsubstring(1, HEXDATA)) / 2) 
    sASN = sASN & _ 
           "A01F06092B0601040182371901" & _ 
           aASNsubstring(1, HEX_TYPE) & _ 
           "120410" & _ 
           aASNsubstring(1, HEXDATA) 
end if 
' 
' Write the ASN.1 blob into a file 
' 
Set oFile = oFilesystem.CreateTextFile(sHostname & ".asn") 
' 
' Put sequence, total length and ASN1 blob into the file 
' 
oFile.WriteLine "30" & ComputeASN1 (Len(sASN) / 2) & sASN 
oFile.Close 
'  
' Use certutil to convert the hexadecimal string into bin 
' 
oShell.Run "certutil -f -decodehex " & sHostname & ".asn " & _  
                                       sHostname & ".bin", 0, True 
'  
' Use certutil to convert the bin into base64 
' 
oShell.Run "certutil -f -encode " & sHostname & ".bin " & _ 
                                    sHostname & ".b64", 0, True 
'############################################################################## 
' 
' Create the INF file 
' 
'############################################################################## 
Set iFile = oFilesystem.OpenTextFile(sHostname & ".b64") 
Set oFile = oFilesystem.CreateTextFile(sHostname & ".inf") 
oFile.WriteLine "[Version]" 
oFile.WriteLine "Signature= " & Chr(34) & "$Windows NT$" & Chr(34) 
oFile.WriteLine "" 
oFile.WriteLine "[NewRequest]" 
oFile.WriteLine "KeySpec = 1" 
oFile.WriteLine "KeyLength = 1024" 
oFile.WriteLine "Exportable = TRUE" 
oFile.WriteLine "MachineKeySet = TRUE" 
oFile.WriteLine "SMIME = FALSE" 
oFile.WriteLine "PrivateKeyArchive = FALSE" 
oFile.WriteLine "UserProtected = FALSE" 
oFile.WriteLine "UseExistingKeySet = FALSE" 
oFile.WriteLine "ProviderName = " & Chr(34) & _ 
                "Microsoft RSA SChannel Cryptographic Provider" & Chr(34) 
oFile.WriteLine "ProviderType = 12" 
oFile.WriteLine "RequestType = PKCS10" 
oFile.WriteLine "KeyUsage = 0xa0" 
oFile.WriteLine "" 
oFile.WriteLine "[EnhancedKeyUsageExtension]" 
oFile.WriteLine "OID=1.3.6.1.5.5.7.3.1" 
oFile.WriteLine "OID=1.3.6.1.5.5.7.3.2" 
oFile.WriteLine ";" 
oFile.WriteLine "; The subject alternative name (SAN) can be included in the INF-file" 
oFile.WriteLine "; for a Windows 2003 CA." 
oFile.WriteLine "; You don't have to specify the SAN when submitting the request." 
oFile.WriteLine ";" 
oFile.WriteLine "[Extensions]" 
iLine = 0 
Do While iFile.AtEndOfStream <> True 
    sLine = iFile.Readline 
    If sLine = "-----END CERTIFICATE-----" then 
        Exit Do 
    end if 
    if sLine <> "-----BEGIN CERTIFICATE-----" then 
        if iLine = 0 then 
            oFile.WriteLine "2.5.29.17=" & sLine 
        else 
            oFile.WriteLine "_continue_=" & sLine 
        end if 
        iLine = iLine + 1 
    end if 
Loop 
oFile.WriteLine "Critical=2.5.29.17" 
oFile.WriteLine ";" 
oFile.WriteLine "; The template name can be included in the INF-file for any CA." 
oFile.WriteLine "; You don't have to specify the template when submitting the request." 
oFile.WriteLine ";" 
oFile.WriteLine ";[RequestAttributes]" 
oFile.WriteLine ";CertificateTemplate=" & sTemplateName 
oFile.Close 
iFile.Close 
'############################################################################## 
' 
' Create the certreq.exe command-line to submit the certificate request 
' 
'############################################################################## 
Set oFile = oFilesystem.CreateTextFile(sHostname & "-req.bat") 
oFile.WriteLine "CERTREQ -attrib " _ 
                 & Chr(34) & "CertificateTemplate:" & sTemplateName _ 
                 & Chr(34) & " " & sHostname & ".req" 
' 
' The GUID structure needs to be reconstructed. The GUID is read 
' as a string like f4aaa8576e6828418712b6ca89fbf5bc however the  
' format that is required for the certreq command looks like 
' 57a8aaf4-686e-4128-8712-b6ca89fbf5bc. The bytes are reordered 
' in the following way: 
' 
'                            11111111112222222222333  
'             Position 12345678901234567890123456789012 
'                      |------|--|--|--|--------------| 
' Original GUID:       f4aaa8576e6828418712b6ca89fbf5bc 
' 
'                            11 1 1111 1112 222222222333                
'             Position 78563412 1290 5634 7890 123456789012                     
'                      |------- |--- |--- |--- |----------| 
' Reformatted GUID:    57a8aaf4-686e-4128-8712-b6ca89fbf5bc 
' 
oFile.WriteLine "REM " 
oFile.WriteLine "REM !!! Only valid for Windows 2003 or later versions !!!" 
oFile.WriteLine "REM If you do not specify certificate extensions in the *.INF file" 
oFile.WriteLine "REM they can be specified here like the following example" 
oFile.WriteLine "REM " 
oFile.WriteLine "REM CERTREQ -submit -attrib " _ 
                 & Chr(34) & "CertificateTemplate:" & sTemplateName _ 
                 & "\n" _ 
                 & "SAN:guid=" _ 
                 & Mid(sGUID, 7, 2) _ 
                 & Mid(sGUID, 5, 2) _ 
                 & Mid(sGUID, 3, 2) _ 
                 & Mid(sGUID, 1, 2) & "-" _ 
                 & Mid(sGUID, 11, 2) _ 
                 & Mid(sGUID, 9, 2) & "-" _ 
                 & Mid(sGUID, 15, 2) _ 
                 & Mid(sGUID, 13, 2) & "-" _ 
                 & Mid(sGUID, 17, 4) & "-" _ 
                 & Mid(sGUID, 21, 12) _ 
                 & "&DNS=" & sDNShostname & Chr(34) & " " & sHostname & ".req" 
oFile.Close 
'############################################################################## 
' 
' Create the certificate verification script 
' 
'############################################################################## 
Set oFile = oFilesystem.CreateTextFile(sHostname & "-vfy.bat") 
oFile.WriteLine "certutil -viewstore " & Chr(34) & objDC.distinguishedname & _ 
                "?usercertificate" & chr(34) 
oFile.Close 
'############################################################################## 
' 
' Compute the ASN1 string 
' 
'############################################################################## 
Function ComputeASN1 (iStrLen) 
    If Len(Hex(iStrLen)) Mod 2 = 0 then 
        sLength = Hex(iStrLen) 
    else 
        sLength = "0" & Hex(iStrLen) 
    end if 
    if iStrLen > 127 then 
        ComputeASN1 = Hex (128 + (Len(sLength) / 2)) & sLength 
    else 
        ComputeASN1 = sLength 
    End If 
End Function

FixDCtemplate.vbs

The fixDCtemplate.vbs script simplifies a change that is required for any certificate template that needs to accept the subject and subject alternative name as part of a certificate request. To change the msPKI-Certificate-Name-Flag in the certificate templates object, Active Directory Service Interfaces (ADSI) is used.

The msPKI-Certificate-Name-Flag attribute is actually a bit field, where the first bit determines whether the template accepts the subject and subject alternative name. Before changing the bit, the script determines if the bit is already set.

Set oArgs = WScript.Arguments 
' 
' Parse command line 
' 
if oArgs.Count <> 1 then 
    Wscript.Echo "fixdctemplate {templatename}" 
    WScript.Quit 1 
end if 
Set objRoot = GetObject("LDAP://rootDSE") 
Set objTemplate = GetObject("LDAP://CN=" & oArgs(0) & _ 
                               ",CN=Certificate Templates," & _ 
                               "CN=Public Key Services,CN=Services," & _ 
                               objRoot.Get("configurationNamingContext")) 
iNameFlag = objTemplate.Get("msPKI-Certificate-Name-Flag") 
if iNameFlag = 1 then 
    Wscript.Echo "Flag is already set" 
else 
    objTemplate.Put("msPKI-Certificate-Name-Flag"), 1     
    objTemplate.SetInfo 
    Wscript.Echo "Flag was set" 
end if