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