Public/New-DynamicDigitalSignature.ps1
|
<#
.SYNOPSIS Generates a dynamic SHA-256 digital signature for ACTIVENet API requests. .DESCRIPTION This function generates a SHA-256 hash based digital signature by combining the API key, shared secret key, and Unix timestamp in epoch seconds (API_KEY + SHARED_SECRET + EPOCH_SECONDS). Enterprise hardening features: - Accepts credentials as either [System.Security.SecureString] or standard [string]. - Guarantees immediate zero-freeing of unmanaged BSTR memory via [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR. - Uses modern [System.Security.Cryptography.SHA256]::Create() (no deprecated SHA256Managed). - Returns a deterministic 64-character lowercase hexadecimal hash. - Preserves backward-compatible parameter names and aliases (-ApiKey, -Secret, -API_Key, -Shared_Secret, -SharedSecretKey). .PARAMETER ApiKey Your ACTIVENet API key as [System.Security.SecureString] or [string]. This parameter is required. .PARAMETER Secret Your ACTIVENet shared secret key as [System.Security.SecureString] or [string]. This parameter is required. .PARAMETER EpochSeconds Optional Unix timestamp in epoch seconds. Defaults to current UTC epoch seconds ([DateTimeOffset]::UtcNow.ToUnixTimeSeconds()). .OUTPUTS [string] Returns a 64-character lowercase hexadecimal SHA-256 signature string. .EXAMPLE PS> $signature = New-DynamicDigitalSignature -ApiKey 'your_api_key' -Secret 'your_shared_secret' .EXAMPLE PS> $signature = New-DynamicDigitalSignature -ApiKey $secApiKey -Secret $secSharedSecret .EXAMPLE PS> # Using legacy v1 parameter names PS> $signature = New-DynamicDigitalSignature -API_Key 'your_api_key' -SharedSecretKey 'your_shared_secret' #> function New-DynamicDigitalSignature { [CmdletBinding()] [OutputType([string])] param ( [Parameter(Mandatory = $true, Position = 0)] [Alias('API_Key', 'Key')] [object]$ApiKey, [Parameter(Mandatory = $true, Position = 1)] [Alias('SharedSecretKey', 'Shared_Secret', 'SharedSecret')] [object]$Secret, [Parameter(Mandatory = $false, Position = 2)] [Alias('timestamp')] [int64]$EpochSeconds = 0 ) # Self-contained helper fallback for standalone script execution $extractString = { param([object]$val, [string]$paramName) if ($null -eq $val) { throw "$paramName cannot be null." } if ($val -is [System.Security.SecureString]) { $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($val) try { return [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { if ($bstr -ne [System.IntPtr]::Zero) { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } } elseif ($val -is [string]) { return [string]$val } else { throw "$paramName must be a [System.Security.SecureString] or [string], received [$($val.GetType().FullName)]." } } $plainKey = & $extractString $ApiKey 'ApiKey' $plainSecret = & $extractString $Secret 'Secret' if ($EpochSeconds -le 0) { $EpochSeconds = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() } $payload = "$plainKey$plainSecret$EpochSeconds" $bytes = [System.Text.Encoding]::UTF8.GetBytes($payload) $sha256 = [System.Security.Cryptography.SHA256]::Create() try { $hashBytes = $sha256.ComputeHash($bytes) return ([System.BitConverter]::ToString($hashBytes) -replace '-', '').ToLowerInvariant() } finally { $sha256.Dispose() } } |