Functions/Set-KESignature.ps1

function Set-KESignature {
<#
.SYNOPSIS
    Calculates the signature for a Kraken API request.
 
.DESCRIPTION
    Calculates the signature for a Kraken API request using the specified payload, URI, and API secret.
 
.PARAMETER Payload
    The payload for the API request, as an OrderedDictionary.
 
.PARAMETER URI
    The URI for the API request, as a string.
 
.PARAMETER api_secret
    The API secret for the Kraken account, as a string.
.NOTES
    Both [System.Security.Cryptography.HashAlgorithm]::Create('SHA256') and [System.Security.Cryptography.SHA256]::Create() create an instance of the SHA256 hashing algorithm provided by the .NET Framework's System.Security.Cryptography namespace. The difference between them is that the former creates an instance of the HashAlgorithm class and the latter creates an instance of the SHA256 class, which inherits from HashAlgorithm.
    The HashAlgorithm class is an abstract base class that provides the basic functionality common to all hash algorithms. It defines the ComputeHash method, which computes the hash value for a given input, as well as other methods and properties that are common to all hash algorithms. When you create an instance of the SHA256 algorithm using [System.Security.Cryptography.HashAlgorithm]::Create('SHA256'), you are creating an instance of the SHA256Managed class, which is a concrete implementation of the SHA256 algorithm that inherits from HashAlgorithm.
    On the other hand, when you create an instance of the SHA256 algorithm using [System.Security.Cryptography.SHA256]::Create(), you are creating an instance of the SHA256Cng class, which is a concrete implementation of the SHA256 algorithm that inherits directly from SHA256.
    In general, it is recommended to use the specific algorithm class, such as SHA256, rather than the HashAlgorithm base class, as it provides a more strongly-typed interface and may have additional functionality specific to that algorithm. However, both methods should work fine for creating an instance of the SHA256 algorithm.
    [System.Security.Cryptography.HMAC]::Create("HMACSHA512") - it works too.LINK
    Specify a URI to a help page, this will show when Get-Help -Online is used.
.EXAMPLE
    PS C:\> $payload = [System.Collections.Specialized.OrderedDictionary]::new()
    PS C:\> $payload.Add("nonce", [int64]([DateTime]::UtcNow - (New-Object DateTime 1970, 1, 1, 0, 0, 0, 0, ([DateTimeKind]::Utc))).TotalMilliseconds
    PS C:\> $payload.Add("ordertype", "limit")
    PS C:\> $payload.Add("type", "buy")
    PS C:\> $payload.Add("pair", "XXBTZUSD")
    PS C:\> $payload.Add("price", "9000")
    PS C:\> $payload.Add("volume", "0.01")
    PS C:\> Set-APIKrakenSignature -Payload $payload -URI "/0/private/AddOrder" -api_secret "KrakenAPIsecret"
 
.NOTES
    The KrakenExchange PowerShell module is not affiliated with or endorsed by Kraken exchange.
    Author: wnapierala [@] hotmail.com, chatGPT
    Date: 03.2023
#>


    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The payload for the API request, as an OrderedDictionary.")]
        [System.Collections.Specialized.OrderedDictionary]$Payload,

        [Parameter(Mandatory = $true, HelpMessage = "The URI for the API request, as a string.")]
        [string]$URI,

        [Parameter(Mandatory = $false, HelpMessage = "The API secret for the Kraken account, as a securestring.")]
        [string]$ApiSecret = ([Environment]::GetEnvironmentVariable('KE_API_SECRET',"User"))
    )
    Write-Debug $MyInvocation.ScriptName
    Write-Debug "APIKey env.: $([Environment]::GetEnvironmentVariable('KE_API_KEY', "User"))"
    Write-Debug "APIKey arg.: ${ApiKey}"
    Write-Debug "APISecret env.: $([Environment]::GetEnvironmentVariable('KE_API_SECRET', "User"))"
    Write-Debug "APISecret arg.: ${ApiSecret}"

    if (-not $ApiSecret) {
        [securestring]$ApiSecret = Read-Host "API Secret" -AsSecureString 
        [string]$ApiSecretEncoded = $ApiSecret | ConvertFrom-SecureString
        [Environment]::SetEnvironmentVariable("KE_API_SECRET", $ApiSecretEncoded, "User")
    } else {
        $ApiSecretEncoded = $ApiSecret
    }
   
    $ApiSecretPlainText = $ApiSecretEncoded | ConvertTo-SecureString | ConvertFrom-SecureString -AsPlainText

    # Convert the payload to a URL-encoded string
    $url_encoded_payload = ($payload.GetEnumerator() | ForEach-Object { $_.Name + "=" + $_.Value }) -join "&"

    # Create an instance of the SHA256 hashing algorithm
    $sha = [System.Security.Cryptography.SHA256]::Create()

    # Compute the SHA256 hash of the nonce and URL-encoded payload
    $shahash = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Payload["nonce"].ToString() + $url_encoded_payload))

    # Create an instance of the HMAC-SHA512 algorithm
    $mac = New-Object System.Security.Cryptography.HMACSHA512


    # Set the key to the API secret converted to bytes
    $api_secret_bytes = [System.Convert]::FromBase64String($ApiSecretPlainText)
    $mac.Key = $api_secret_bytes

    # Compute the HMAC-SHA512 hash of the URI and SHA256 hash
    $machash = $mac.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($URI) + $shahash)

    # Convert the result to a Base64-encoded string and return it
    $signature = [System.Convert]::ToBase64String($machash)
    return $signature
}