Code signing using PowerShell Scripting

Scenario – You have a requirement to sign the output of your application. The output can be in the form of .dll, .exe, .ocx etc. You need to sign all of them before distributing them to others.

Requirements – To implement the code signing, you will need to things –

  1. Security certificate
  2. Timestamp server URL

You need to install certificate on the server where you want to sign your code from. Insure that the certificate should be non-exportable or else some one will export it and use it.

Timestamp is needed to ensure the validity of the signed code.

Implementation – With the above requirements in place, we can use the below PowerShell Cmdlets to sign the code –

  1. Set-AuthenticodeSignature – This is to add the authenticode signature to the file.
  2. Get-AuthenticodeSignature – This is to get information about the Authenticode signature in the file. With this cmdlet, you can find if the file has valid signature or not. 

I have written below script to sign any .dll, .exe, .ocx files present in the base location where this script is placed, if they don’t have valid signature.

It does below activities –

  1. It is reading certificate from the store on the server.
  2. It is then finding the certificate that we want to use from the output of above command and then storing it into the variable to be used.
  3. There is a command line in the script to get the full path of all the files (.dll, .ocx, .exe) from all the folders and subfolders where the script is kept.
  4. It is then finding if the file (with full path) as found in step 3 above has a valid signature or not. If it has valid signature, it is ignoring this file and going to next file. If the file is not signed, it is calling function “Codesigning()” to sign it.
  5. All the activities are captured in the log file at the same location as well as displaying on the console.

Here is the PowerShell Script –

<#
.Synopsis
   Script is to sign all internal built (.dll, .exe, .ocx) file outputs
#>

function CodeSigning ()
{
    Param (
            [Parameter(Mandatory=$True)]
            [ValidateNotNull()]
            $FileNameWithPath,           
            [Parameter(Mandatory=$True)]
            [ValidateNotNull()]
            $CertInfo
          )

    write-host "———————————————"
    Write-Host "FileName – " $FileNameWithPath
    Write-Host "Code Signing Started–"
    Set-AuthenticodeSignature $FileNameWithPath $CertInfo -TimestampServer
http://TimeStampURL
    Write-Host "Code Signing Finished Successfully"
    write-host "———————————————"
}

Start-Transcript -Path ".\codesigningTrans.log"
$cert= (dir cert:localmachine\my\ -CodeSigningCert)
write-host "————All Certificate Information from the server———-"
write-host $cert
write-host "———————————————"
foreach ($_cert in $cert)
{
    if($_cert.Thumbprint -eq "ReplaceWithACTUAL")
    {
        $CertInfo = $_cert
        Write-Host "————Certificate in Use start————–"
        Write-Host $CertInfo
        Write-Host "————Certificate in Use End————–"
        $FileData = Get-ChildItem -rec | Where {$_.Extension -in ".dll",".exe",".ocx"}  | ForEach-Object -Process {$_.FullName}

        foreach ($_FileData in $FileData)
        {
            $FileNameWithPath = $_FileData           
            $IsValid = Get-AuthenticodeSignature  $FileNameWithPath | where {$_.Status -eq "Valid"}
            if (!$IsValid.Status -eq "Valid")
            {
                CodeSigning $FileNameWithPath $CertInfo
            }
            else
            {
                Write-Host $FileNameWithPath " already has valid signature"
            }
        }  
    }
}
Stop-Transcript
$log = Get-Content ".\codesigningTrans.log"
$log > ".\codesigningTrans.log"

————————-End of Article———————-

Implementing UNIX Grep functionality in PowerShell

The grep UNIX command allows you to find lines in files that contain key words or phrases. With this command, it is possible to perform a quick search of a file or directory without having to look at each file via a text editor or the UNIX more command.

To achieve same functionality in PowerShell, we can use Select-String cmdlet as shown below:

dir -Recurse *.* | Select-String -Pattern "Clear" | Select -Unique path

This will list all the files with path that contains “Clear” as string inside it.

Below command line will look for all the patterns available in Pattern.txt and will list the matching files with the path.

dir -Recurse *.* | Select-String -Pattern (Get-Content .\Pattern.txt) | Select -Unique path

–End of Article–

PS Script for replacing strings in the File

If you have a requirement to replace particular string in a file with some other string, it can be done using below PS statements.

(Get-Content .\test.txt | ForEach-Object {$_ -replace "CurrentUser","CurrentUser2"}) | Set-Content .\test.txt

Explanation –

1. First step is to read the content of the file

2. Pipe the output. Read each line by line and replace the required value. Note – “$_” means this row. It is equivalent to “this.” in .Net.

3. Please note “(“and “)” at the start of first command and end of second command respectively. It is needed as we want to send (Pipe) the full output to the last statement to set the content.

4. Finally set the updated content to the same file.

PowerShell – Zip Files and Send Email with Attachment

Hi Guys…

Suppose, you have below requirements:

1. To create a zip file containing all txt and dll files from a particular folder

2. To send the above newly created Zip file to the respective person in an email as an attachment.

—–

I have achieved it by creating the below script: Here in the script, I created two functions – CreateZip and SendEmail.

Script Body –

——————

<#
.Synopsis
   Create Zip file and then sent it as attachment in an email.
.DESCRIPTION
   This script will create a zip file and then will send this zip file as an attachment to an email.
.EXAMPLE
#>

#Script Name – SendEmailWithZipAttachment.ps1
#Creator – Mohd Aslam Ansari
#Date – 30-Dec-15
#Updated – First Version
#References, if any

$LogSource = "C:\temp\"
$ZipFileName = "Log.zip"

CreateZip $LogSource $ZipFileName
SendEmail

Function CreateZip {

New-Item -Force -ItemType directory -Path $LogSource\temp
Get-ChildItem $LogSource\* -Include *.txt,*.dll | ForEach-Object {Copy-Item $_ $LogSource\temp}

if (Test-Path -path $LogSource$ZipFileName)
{
    Remove-Item -Force $LogSource$ZipFileName
}

Add-Type -AssemblyName "System.IO.Compression.FileSystem"
[System.IO.Compression.ZipFile]::CreateFromDirectory($LogSource+’temp’, $LogSource+$ZipFileName)

Remove-Item -Force -Recurse $LogSource\temp
}

Function SendEmail {

$Email = New-Object -ComObject "CDO.Message"
$Email.Configuration.Fields.Item("
http://schemas.microsoft.com/cdo/configuration/sendusing") = 2
$Email.Configuration.Fields.Item("
http://schemas.microsoft.com/cdo/configuration/smtpserver") = ‘xyz.smtp.com’
$Email.Configuration.Fields.Item("
http://schemas.microsoft.com/cdo/configuration/smtpserverport") = 25
$Email.Configuration.Fields.Update()

$Email.From="xyz@xyz.com"
$Email.To="abc@xyz.com"
$Email.Subject="Test email"
$Email.AddAttachment($LogSource+$ZipFileName)
$Email.TextBodyPart = "PFA…"

$Email.Send()
}

—End of Article—

Mapping Drive using PowerShell

Suppose we have requirement to Map a drive to a UNC path that user needs to input. How to do it?

Here is my script that can do it for you…

Below script will ask for the User Input and then will map the “K” drive to that path.

image

On clicking “Ok” it will map. If the drive is already mapped, it will give below error message:

image

Script:

—————–

<#
.Synopsis
   Function to map the drive
.DESCRIPTION
   This script will map the drive to UNC entered by the User.
.EXAMPLE
   ./Drivemapping.ps1
#>

#Script Name – Drivemapping.ps1
#Creator – Mohd Aslam Ansari
#Date – 23-Dec-15
#Updated – First Version
#References, if any – I have used New-PSDrive function to map the drive.

Function DriveMapping
{

Param (
      [string] $drive,    
      [string] $UNCLoc
      )

$driveInfo = New-Object System.IO.DriveInfo($drive)
    if (-Not $driveInfo.IsReady)
    {
        New-PSDrive -Name $drive -Root $UNCLoc -Persist -PSProvider FileSystem -scope Global -Verbose
        [Microsoft.VisualBasic.Interaction]::MsgBox("UNC Path ‘"  + $UNCLoc + "’ mapped to " + $drive + " drive successfully.","OKOnly,SystemModal,Information", "Success")
    }
    else
    {
        Write-Output "Drive not ready/available for mapping"
        [Microsoft.VisualBasic.Interaction]::MsgBox("Drive not ready/available for mapping.","OKOnly,SystemModal,Critical", "Error")
    }
}

[void] [System.Reflection.Assembly]::LoadWithPartialName("Microsoft.VisualBasic")
[string]$drive = "K"
[string]$UNCLoc = [Microsoft.VisualBasic.Interaction]::InputBox("Enter a UNC path to be Mapped–>", "Path to be Mapped to " + $drive + " drive","")

DriveMapping -drive $drive -UNCLoc $UNCLoc

—————-

I used New-PSDrive cmdlet to create it. Please refer to PowerShell help to read more about it.

–End of Article–

PowerShell DSC – How to install a MSI

PowerShell DSC – DSC is a new management platform in Windows PowerShell that enables deploying and managing configuration data for software services and managing the environment in which these services run.

Requirement – How to install MSI using PowerShell DSC?

Solution –

Suppose, we have xyz.client.msi that needs to be installed on target machine. For this example, I have considered installing it on the localhost. To install it on the target server, PS remoting needs to be enabled and WinRM port configured for HTTP communication using below PowerShell commands

a) Enable-PSRemoting –Force

b) winrm quickconfig –transport:http

I wrote below script to make it happen:

Configuration ApplicationInstall
{

Package AppInstall
{
    Ensure = "Present"
    Path = "E:\Software\xyz.Client.msi"
    Name = "TestPkg"
    ProductId = "{23FB7DAE-F831-4F6F-854B-2B28A7DD3C98}"
    Arguments = "AllUsers=1"
}
}

ApplicationInstall –Output C:\ApplicationInstall

Start-DscConfiguration -Path C:\ApplicationInstall -ComputerName localhost -Force -Verbose -wait

Explanation –

First thing, we need to create a Configuration with the key word “Configuration”.

Next step is to create the “Package” resource. It has many properties as explained below:

Package [string] #ResourceName
{
Name = [string] #This is Mandatory Property
Path = [string] #This is Mandatory Property
ProductId = [string] #This is Mandatory Property
[ Arguments = [string] ]
[ Credential = [PSCredential] ]
[ DependsOn = [string[]] ]
[ Ensure = [string] { Absent | Present } ]
[ LogPath = [string] ]
[ ReturnCode = [UInt32[]] ]
}

Apart from Mandatory parameters, I have used:

1) Ensure parameter –

        “Present” – Means the MSI should be present, if not, it will install it.

        “Absent” – Means the MSI should not be present, if present, it will uninstall it.

2) Arguments – You can pass any argument that you want to pass with MSI.

Once the Package is written, we need to execute the configuration with the following command and it will create the MOF (Managed Object Format) file and will keep it at C:\ApplicationInstall folder:

ApplicationInstall –Output C:\ApplicationInstall

image

Content of MOF file –

image

MOF files are a convenient way to change WMI settings and to transfer WMI objects between computers. The contents of MOF files only become effective when the files are compiled.

Now to compile MOF file, I ran the below command:

Start-DscConfiguration -Path C:\ApplicationInstall -ComputerName localhost -Force -Verbose –wait

Output –

image

If you re-ran the above command, it will say, “the package is already installed”.

If you want to uninstall the application, just change the “Ensure” property of the package to “Absent”, and then execute the configuration to create the MOF file and then compile it. It will then uninstall the application.

—End of Article—