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

  1. Using Powershell Script, we can able to request and create certificates from your CA Certificate Server.
  2. We need to include the “SAN” attribute from windows 2016 onwards.

Chef Programing

  1. Certificate.rb Ruby file will copy the Powershell ERB file on to the servers.
  2. Using windows_task to run Powershell script since window_task run with elevated SYSTEM permissions which requires a script to execute.
  3. 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