As per PCI requirements, we need to secure the Web server URL. Therefore we need to replace the built-in self-signed certificate for the Web server on Windows with your specific certificate.
To Create Self-Singed Certificate
- Using Powershell Script, we can able to request and create certificates from your CA Certificate Server.
- We need to include the “SAN” attribute from windows 2016 onwards.
Chef Programing
- Certificate.rb Ruby file will copy the Powershell ERB file on to the servers.
- Using windows_task to run Powershell script since window_task run with elevated SYSTEM permissions which requires a script to execute.
- Instead of window_binding, I am using netsh http cmd script to apply the certificate thumbprint.
Certificate.rb
certs = 'C:\\Certs'
cert_group = "Your Certitifate Group"
if policy_group_arr[1].start_with?("Pre")
#CA server used for domain
ca_name = 'CASERVER\USERLOGIN'
elsif policy_group_arr[1].start_with?("Prod")
#CA server used for domain
ca_name = 'CASERVER\USERLOGIN'
else
# CA server used for domain
ca_name = 'CASERVER\USERLOGIN'
end
log "<<< ca name : #{ca_name} >>>"
env_caname = "#{ca_name}"
# log '**** Creating Powershell Script to create Certificates'
template "#{certs}\\request_certificate.ps1" do
source "request_certificate.erb"
variables(
product_name: cert_group.to_s,
)
action :create
end
# log '**** Creating DOS Bash Script to apply netsh binding with Certificate Thumbprint'
template "#{certs}\\ApplyNetshBinding.cmd" do
source "ApplyNetshBinding.erb"
variables(
product_name: cert_group.to_s,
)
action :create
end
# It Require SYSTEM privilege to this powershell script to request certicate using correct CA
windows_task "request_certificate." do
task_name "request_certificate"
command "#{certs}\\request_certificate.ps1"
action [:create, :run, :delete]
run_level :highest
frequency :once
start_time "12:57"
force true
not_if { File.exist?("#{certs}\\request_certificate.log") }
end
# Window system user account is required here to apply netsh binding to Service appID.
windows_task "ApplyNetshBinding" do
task_name "ApplyNetshBinding"
command "#{certs}\\ApplyNetshBinding.cmd"
action [:create, :run, :delete]
run_level :highest
frequency :once
start_time "13:57"
force true
not_if { File.exist?("#{certs}\\ApplyNetshBinding.log") }
end
Request_certificate.erb
#
Title: Request & Create Certificates on LocalMachine using CAName Domain Name.
Author: Sandeep R Narani
Date: 2/20/2019
HISTORY: 1/20/2020, NARS01, Initial version
Command Usage ./request_certificate.ps1 -CAName <'CA Name'>
For example: ./request_certificate.ps1 -CAName 'DBADEEDS.COM\DEEDS DBA'
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$False,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[string]$CN=$env:computername.ToLower() + "." + $env:USERDOMAIN.ToLower() + ".com",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string[]]$SAN = "dns=$CN",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[String]$TemplateName = "WebServer",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string]$CAName,
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[switch]$Export,
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string]$Country="US",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string]$State="GA",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string]$City="Alanta",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string]$Organization="DBA",
[Parameter(Mandatory=$False,ValueFromPipelineByPropertyName=$True)]
[string]$Department="<%= @product_name %>"
)
BEGIN {
#Function that logs a message to a text file
$LogFile = "C:\Certs\RequestAndCreate.log"
#Delete log file if it exists
if(Test-Path $LogFile)
{
Remove-Item $LogFile
}
#Start Logging
Start-Transcript -Path $LogFile
#internal function to do some cleanup
function Remove-ReqTempfiles()
{
param(
[String[]]$tempfiles
)
Write-Verbose "Cleanup temp files and pending requests"
#delete pending request (if a request exists for the CN)
$certstore = new-object system.security.cryptography.x509certificates.x509Store('REQUEST', 'LocalMachine')
$certstore.Open('ReadWrite')
foreach($certreq in $($certstore.Certificates))
{
if($certreq.Subject -eq "CN=$CN")
{
$certstore.Remove($certreq)
}
}
$certstore.close()
foreach($file in $tempfiles){remove-item ".\$file" -ErrorAction silentlycontinue}
}
}
PROCESS {
#disable debug confirmation messages
if($PSBoundParameters['Debug']){$DebugPreference = "Continue"}
#check if SAN certificate is requested
if($SAN)
{
#each SAN must be a array element
#if the array has ony one element then split it on the commas.
if(($SAN).count -eq 1)
{
$SAN = $SAN -split ","
#formating $SAN to correct format
$SAN = $SAN -join "&"
Write-Host "Requesting SAN certificate with subject $CN and SAN: $SAN" -ForegroundColor Green
Write-Debug "Parameter values: CN = $CN, TemplateName = $TemplateName, CAName = $CAName, SAN = $SAN"
}
}
else
{
Write-Host "Requesting certificate with subject $CN" -ForegroundColor Green
Write-Debug "Parameter values: CN = $CN, TemplateName = $TemplateName, CAName = $CAName"
}
Write-Verbose "Generating request inf file"
$file = @"
[NewRequest]
Subject = "CN=$CN,c=$Country, s=$State, l=$City, o=$Organization, ou=$Department"
MachineKeySet = TRUE
KeyLength = 2048
KeySpec=1
Exportable = TRUE
RequestType = PKCS10
FriendlyName = "$CN"
[RequestAttributes]
CertificateTemplate = "$TemplateName"
"@
#be sure that no of the tempfile exists already
Remove-ReqTempfiles -tempfiles "certreq.inf","certreq.req","$CN.cer","$CN.rsp"
#create new request inf file
Set-Content .\certreq.inf $file
#show inf file if -verbose is used
Get-Content .\certreq.inf | Write-Verbose
try {
Write-Verbose "generate .req file with certreq.exe"
Invoke-Expression -Command "certreq -new certreq.inf certreq.req"
if(!($LastExitCode -eq 0))
{
throw "certreq -new command failed"
}
write-verbose "sending certificate request to CA"
Write-Verbose "A value for the SAN is specified. Requesting a SAN certificate."
Write-Debug "CAName = $CAName"
if($SAN)
{
Write-Debug "certreq -attrib `"SAN:$SAN`" -submit -config `"$CAName`" certreq.req $CN.cer"
if($CAName)
{
Invoke-Expression -Command "certreq -attrib `"SAN:$SAN`" -submit -config `"$CAName`" certreq.req $CN.cer"
}
else
{
Invoke-Expression -Command "certreq -attrib `"SAN:$SAN`" -submit certreq.req $CN.cer"
}
}
else
{
Write-Debug "certreq -submit -config `"$CAName`" certreq.req $CN.cer"
if($CAName)
{
Invoke-Expression -Command "certreq -submit -config `"$CAName`" certreq.req $CN.cer"
}
else
{
Invoke-Expression -Command "certreq -submit certreq.req $CN.cer"
}
}
if(!($LastExitCode -eq 0))
{
throw "certreq -submit command failed"
}
Write-Debug "request was successful. Result was saved to $CN.cer"
write-verbose "retreive and install the certifice"
Invoke-Expression -Command "certreq -accept $CN.cer"
if(!($LastExitCode -eq 0))
{
throw "certreq -accept command failed"
}
if(($LastExitCode -eq 0) -and ($? -eq $true))
{
Write-Host "Certificate request successfully finished!" -ForegroundColor Green
}
else
{
throw "Request failed with unkown error. Try with -verbose -debug parameter"
}
if($export)
{
Write-Debug "export parameter is set. => export certificate"
Write-Verbose "exporting certificate and private key"
$cert = Get-Childitem "cert:\LocalMachine\My" | where-object {$_.Thumbprint -eq (New-Object System.Security.Cryptography.X509Certificates.X509Certificate2((Get-Item "$CN.cer").FullName,"")).Thumbprint}
Write-Debug "Certificate found in computerstore: $cert"
#create a pfx export as a byte array
$certbytes = $cert.export([System.Security.Cryptography.X509Certificates.X509ContentType]::pfx)
#write pfx file
$certbytes | Set-Content -Encoding Byte -Path "$CN.pfx" -ea Stop
Write-Host "Certificate successfully exportert to $CN.pfx !" -ForegroundColor Green
Write-Verbose "deleting exported certificat from computer store"
# delete certificate from computer store
$certstore = new-object system.security.cryptography.x509certificates.x509Store('My', 'LocalMachine')
$certstore.Open('ReadWrite')
$certstore.Remove($cert)
$certstore.close()
}
else
{
Write-Debug "export parameter is not set. => script finished"
Write-Host "The certificate with the subject $CN is now installed in the computer store !" -ForegroundColor Green
}
}
catch {
#show error message (non terminating error so that the rest of the piple input get processed)
Write-Error $_
}
finally {
#tempfiles and request cleanup
Remove-ReqTempfiles -tempfiles "certreq.inf","certreq.req","$CN.cer","$CN.rsp"
}
}
END
{
Remove-ReqTempfiles -tempfiles "certreq.inf","certreq.req","$CN.cer","$CN.rsp"
Stop-Transcript
}
ApplyNetshBinding.erb file converts as ApplyNetshBinding.cmd Dos Script to stop the web service and apply netsh http with installed certificate thumbprint and restart the web service
@echo off
:: This CMD script provides to add Certificate Thumbprint Hash Value to netsh with Provided Appid.
:: Author Sandeep Reddy Narani
TITLE Stop Services, Add Netsh Binding and Start Services
set LOGFILE=C:\Certs\ApplyNetshBinding.log
call :LOG > %LOGFILE%
exit /B
:LOG
set GET_IP=powershell -Command "(Get-NetIPConfiguration).IPv4Address.IPAddress"
for /f %%a in ('%GET_IP%') do set SearchString=%%a
set /a ExitCode=0
set GET_THUMBPRINT=powershell -Command "(Get-ChildItem -Path Cert:\LocalMachine\My | Where-Object {$_.Subject -match '<%= @product_name %>'} | Select-Object -ExpandProperty Thumbprint).ToLower()"
for /f %%i in ('%GET_THUMBPRINT%') do set THUMBPRINT=%%i
if %THUMBPRINT == "" (
echo Certificates Not found
) else (
REM Stopping Services
Stop YOUR Web Server Services
echo Certificate Thumbprint found add to netsh http
netsh http show sslcert | find /i "%SearchString%" > NUL
set /a ExitCode=%errorlevel%
if %ExitCode% EQU 0 goto StringFound
REM Program returned an exit code <> 0 - command failed or string not found.
echo Failure: The string "%SearchString%" was not found in this netsh output:
REM Set ExitCode to 1001 so MaxFocus will report error in dashboard.
set /a ExitCode=1001
REM Add new binding Entries
netsh http add sslcert ipport=%SearchString%:3552 certhash=%THUMBPRINT% appid={4dc3e181-e14b-4a21-b022-59fc669b0914} certstorename=My verifyclientcertrevocation=disable
netsh http add sslcert ipport=%SearchString%:443 certhash=%THUMBPRINT% appid={4dc3e181-e14b-4a21-b022-59fc669b0914} certstorename=My verifyclientcertrevocation=disable
goto End
:StringFound
echo Success: The string "%SearchString%" was found in this netsh output:
:End
echo Add Certificate Thumbprint to netsh binding
REM Echo the netsh command and run to show its output
REM Remove existing binding
netsh http delete sslcert ipport=%SearchString%:443
netsh http delete sslcert ipport=%SearchString%:3552
REM Add new binding Entries
netsh http add sslcert ipport=%SearchString%:3552 certhash=%THUMBPRINT% appid={4dc3e181-e14b-4a21-b022-59fc669b0914} certstorename=My verifyclientcertrevocation=disable
netsh http add sslcert ipport=%SearchString%:443 certhash=%THUMBPRINT% appid={4dc3e181-e14b-4a21-b022-59fc669b0914} certstorename=My verifyclientcertrevocation=disable
REM Starting Services
Start YOUR Web Server Services
echo Exiting script with ExitCode = %ExitCode%
exit /b %ExitCode%
)
Leave a comment