Knowledge Base

Preserving for the future: Shell scripts, AoC, and more

Sign-Code.ps1 (Source)

#!/bin/pwsh
# File: Sign-Code.ps1
# Last-Modified: 2026-04-01T11:07:34-0400
# Locations:
#     \\utility5\e$\CodeSign\Scripts
# Author: bgstack15
# Startdate: 2026-03-31
# Title: Sign-Code
# Project: Codesign
# Purpose: Functions for easily signing binaries on utility5
# History:
# Usage:
#    As a library, . .\Sign-Code.ps1
#    Or directly, ".\Sign-Code.ps1"
# References:
#     https://www.scriptinglibrary.com/languages/powershell/how-to-modify-your-proxy-settings-with-powershell/
#     https://4sysops.com/archives/how-to-create-an-open-file-folder-dialog-box-with-powershell/
# Improve:
# Dependencies:
#    Cert loaded in Cert:\LocalMachine\My
# Documentation:
<#
.SYNOPSIS
    Functions for easily signing binaries on the utility server.
.DESCRIPTION
    This script provides functions to configure proxy settings, select files via a dialog,
    sign binaries using signtool.exe, and move signed files to a destination folder.
    It supports being dot-sourced as a library or run directly to invoke the signing process.
.NOTES
.USAGE
    As a library (import functions for manual use):
        . .\Sign-Code.ps1
    Run directly (sign files from default or specified folders):
        .\Sign-Code.ps1
.PARAMETER Set-Proxy
    Configures the user's system proxy settings by modifying registry keys.
.PARAMETER Get-File-Dialog
    Opens a Windows File Open dialog to select one or more files.
.PARAMETER Sign-File
    Signs each file using signtool.exe and moves successfully signed files to a destination folder.
.PARAMETER Ask-Sign-Files
    Main function to select files, and sign them.
.DEPENDENCIES
    - Windows signtool.exe located at the specified path.
    - Code signing certificate loaded in Cert:\LocalMachine\My.
    - User has write permissions to destination folders.
.REFERENCES
    - https://www.scriptinglibrary.com/languages/powershell/how-to-modify-your-proxy-settings-with-powershell/
    - https://4sysops.com/archives/how-to-create-an-open-file-folder-dialog-box-with-powershell/
#>
#region Proxy Configuration Function
Function Set-Proxy {
    <#
    .SYNOPSIS
        Sets the system proxy server and port for the current user.
    .PARAMETER Server
        The proxy server address (e.g., proxy.example.com).
    .PARAMETER Port
        The proxy server port number (e.g., 8080).
    .EXAMPLE
        Set-Proxy -Server proxy.example.com -Port 8080
    #>
    [CmdletBinding()]
    [Alias('proxy')]
    [OutputType([string])]
    Param
    (
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 0)]
        [string]$Server,
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true,
            Position = 1)]
        [int]$Port
    )
    # Set proxy server address and enable proxy in the user's registry
    Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -Name ProxyServer -Value "$($Server):$($Port)"
    Set-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' -Name ProxyEnable -Value 1
    # (Optional) Display the current proxy settings
    # Get-ItemProperty -Path 'HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings' | Select-Object ProxyServer, ProxyEnable
}
#endregion Proxy Configuration Function
#region File Selection Function
Function Get-File-Dialog {
    <#
    .SYNOPSIS
        Opens a file open dialog for the user to select one or multiple files.
    .PARAMETER InitialFolder
        The folder that the dialog will open initially.
    .OUTPUTS
        String[] - Full paths of the selected files.
    .EXAMPLE
        $files = Get-File-Dialog -InitialFolder "E:\CodeSign\ToBeSigned"
    #>
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [string]$InitialFolder = "E:\CodeSign\ToBeSigned"
    )
    # Load Windows Forms assembly for GUI dialog
    Add-Type -AssemblyName System.Windows.Forms
    # Configure and show OpenFileDialog allowing multi-selection
    $FileBrowser = New-Object System.Windows.Forms.OpenFileDialog -Property @{
        InitialDirectory = $InitialFolder;
        Title = "Choose file(s) to sign";
        MultiSelect = $true
    }
    $null = $FileBrowser.ShowDialog()
    # Return array of selected file paths
    return $FileBrowser.FileNames
}
#endregion File Selection Function
#region File Signing Function
Function Sign-File {
    <#
    .SYNOPSIS
        Signs one or more files using signtool.exe and moves successfully signed files.
    .PARAMETER FilePath
        One or more full file paths to sign. Supports pipeline input.
    .PARAMETER DestinationFolder
        Directory where successfully signed files will be moved.
    .NOTES
        Requires signtool.exe at specified path and appropriate certificates installed.
    .EXAMPLE
        $files | Sign-File -DestinationFolder "E:\CodeSign\SignedCode"
    #>
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $true,
                   ValueFromPipeline = $true,
                   ValueFromPipelineByPropertyName = $true,
                   Position = 0)]
        [string[]]$FilePath,
        [Parameter(Mandatory = $true)]
        [string]$DestinationFolder
    )
    Process {
        foreach ($file in $FilePath) {
            Write-Host "Signing file: $file"
            # Execute signtool.exe with specified options
            & "C:\Program Files (x86)\Windows Kits\10\bin\10.0.26100.0\x64\signtool.exe" sign `
                /fd sha256 /td sha256 /v `
                /f "E:\CodeSign\cert2025\example.cer" `
                /sha1 B38EXAMPLE17340436A80B0A2FDC3BEC9352250F `
                /kc CodeSignExample2025 `
                /csp "Safenet Key Storage Provider" `
                /tr http://timestamp.digicert.com `
                $file
            $exitCode = $LASTEXITCODE
            if ($exitCode -eq 0) {
                # Signing succeeded; ensure destination folder exists
                if (-not (Test-Path -Path $DestinationFolder)) {
                    New-Item -Path $DestinationFolder -ItemType Directory -Force | Out-Null
                }
                # Move signed file to destination folder
                $destinationPath = Join-Path -Path $DestinationFolder -ChildPath (Split-Path $file -Leaf)
                try {
                    Move-Item -Path $file -Destination $destinationPath -Force
                    Write-Host "Moved file to $destinationPath"
                }
                catch {
                    Write-Warning "Failed to move file $file to ${destinationPath}: $_"
                }
            }
            else {
                Write-Warning "Signing failed for $file with exit code $exitCode. File not moved."
            }
        }
    }
}
#endregion File Signing Function
#region Main Orchestration Function
Function Ask-Sign-Files {
    <#
    .SYNOPSIS
        Orchestrates proxy setup, file selection, and signing of files.
    .PARAMETER DestinationFolder
        Optional. Destination folder for signed files. Defaults to "E:\CodeSign\SignedCode".
    .PARAMETER InitialFolder
        Optional. Initial folder for the file selection dialog.
    .EXAMPLE
        Ask-Sign-Files -InitialFolder "E:\CodeSign\ToBeSigned" -DestinationFolder "E:\CodeSign\SignedCode"
    #>
    [CmdletBinding()]
    Param(
        [Parameter(Mandatory = $false)]
        [string]$DestinationFolder = "E:\CodeSign\SignedCode",
        [Parameter(Mandatory = $false)]
        [string]$InitialFolder
    )
    # Configure proxy settings for the user environment
    Set-Proxy -Server proxy.example.com -Port 8080
    # Get files to sign via file dialog
    $Infiles = Get-File-Dialog -InitialFolder $InitialFolder
    # Pass files and destination folder to signing function
    $Infiles | Sign-File -DestinationFolder $DestinationFolder
}
#endregion Main Orchestration Function
#region Script Execution Control
# Check if script is being run directly or dot-sourced/imported
# If called directly, with more than 0 arguments
if ($args.Count -gt 0) {
    # Files were dropped onto the script or shortcut, process them directly
    # Assuming the DestinationFolder is set or can be defaulted
    $destinationFolder = "E:\CodeSign\SignedCode"
    # Validate that all args are files
    $filesToSign = $args | Where-Object { Test-Path $_ -PathType Leaf }
    if ($filesToSign.Count -eq 0) {
        Write-Warning "No valid files passed as arguments."
        exit 1
    }
    # Call Sign-File with the files from args
    $filesToSign | Sign-File -DestinationFolder $destinationFolder
}
elseif ($MyInvocation.InvocationName.Contains($MyInvocation.MyCommand.Name) -or
    $MyInvocation.InvocationName -eq $PSCommandPath) {
    # Script is being run directly; invoke main function with default folders
    Ask-Sign-Files -InitialFolder "E:\CodeSign\ToBeSigned" -DestinationFolder "E:\CodeSign\SignedCode"
}
#endregion Script Execution Control