How to Timestamp Files in PowerShell

Qualified Timestamping in PowerShell (.NET Integration)

Learn how to request and verify qualified RFC 3161 timestamp tokens (.tsr) directly in PowerShell 7+ using native .NET cryptographic assemblies without external tools.

Prerequisites & Requirements:

  • PowerShell 7.0+ (Core) recommended (built on .NET 5+).
  • Built-in .NET assembly: System.Security.Cryptography.Pkcs.
  • TSA Certificate Chain (for offline verification): Download TSAChain.zip.

Step 1: Request and Save a Timestamp Token (.tsr)

Compute the SHA-256 hash of your file, generate an Rfc3161TimestampRequest object, send it via Invoke-RestMethod, and write the binary response token to disk.

function Request-Rfc3161Timestamp {
    param (
        [Parameter(Mandatory = $true)]
        [string]$FilePath,

        [Parameter(Mandatory = $true)]
        [string]$OutputTsrPath,

        [Parameter(Mandatory = $true)]
        [string]$TsaUrl
    )

    # 1. Compute SHA-256 hash of the target file
    $fileBytes = [System.IO.File]::ReadAllBytes($FilePath)
    $hash = [System.Security.Cryptography.SHA256]::HashData($fileBytes)

    # 2. Build RFC 3161 request
    $hashAlgorithm = [System.Security.Cryptography.HashAlgorithmName]::SHA256
    $request = [System.Security.Cryptography.Pkcs.Rfc3161TimestampRequest]::CreateFromHash(
        $hash,
        $hashAlgorithm,
        $null,
        $true # Request TSA certificate in response
    )

    $encodedRequest = $request.Encode()

    # 3. Send HTTP POST request to the TSA endpoint
    $tsrBytes = Invoke-RestMethod -Uri $TsaUrl `
        -Method Post `
        -ContentType "application/timestamp-query" `
        -Body $encodedRequest `
        -ResponseHeadersVariable responseHeaders

    # 4. Save timestamp response (.tsr)
    [System.IO.File]::WriteAllBytes($OutputTsrPath, $tsrBytes)
    Write-Host "[SUCCESS] Timestamp saved to: $OutputTsrPath" -ForegroundColor Green
}

Step 2: Verify the Timestamp Token (.tsr)

Parse the saved .tsr response using Rfc3161TimestampToken and validate its hash matching against the original file.

function Test-Rfc3161Timestamp {
    param (
        [Parameter(Mandatory = $true)]
        [string]$FilePath,

        [Parameter(Mandatory = $true)]
        [string]$TsrPath
    )

    # 1. Read original file and compute SHA-256 hash
    $fileBytes = [System.IO.File]::ReadAllBytes($FilePath)
    $hash = [System.Security.Cryptography.SHA256]::HashData($fileBytes)
    $tsrBytes = [System.IO.File]::ReadAllBytes($TsrPath)

    # 2. Parse RFC 3161 Timestamp Token
    $bytesRead = 0
    $token = [System.Security.Cryptography.Pkcs.Rfc3161TimestampToken]::ReadIfTimestampToken(
        $tsrBytes,
        [ref]$bytesRead
    )

    if ($null -eq $token) {
        Write-Error "[ERROR] Invalid timestamp token format."
        return $false
    }

    # 3. Verify hash integrity
    $hashAlgorithm = [System.Security.Cryptography.HashAlgorithmName]::SHA256
    $isHashValid = $token.VerifyHash($hash, $hashAlgorithm, [ref]$null)

    if ($isHashValid) {
        $signingTime = $token.AsSignedCms().SignerInfos[0].SigningTime
        Write-Host "[SUCCESS] Timestamp is valid! Signed at (UTC): $($signingTime.UtcDateTime)" -ForegroundColor Green
        return $true
    } else {
        Write-Error "[ERROR] File hash does not match timestamp imprint."
        return $false
    }
}

Execution Example

Run the functions in your PowerShell session or script file:

$fileToSign = "document.pdf"
$tsrOutput  = "document.pdf.tsr"
$tsaUrl     = "https://tsaexample.com/myserver.aspx"

# 1. Request Timestamp Token
Request-Rfc3161Timestamp -FilePath $fileToSign -OutputTsrPath $tsrOutput -TsaUrl $tsaUrl

# 2. Verify Timestamp Token
$isValid = Test-Rfc3161Timestamp -FilePath $fileToSign -TsrPath $tsrOutput
Write-Host "Verification Result: $isValid"
Why use native PowerShell? PowerShell 7+ exposes .NET’s built-in Rfc3161TimestampRequest classes directly, making it ideal for CI/CD pipelines, automated system deployment, and administrative scripting without installing extra executables.