Functions/New-JwtSignature.ps1

function New-JwtSignature
{
<#
    .SYNOPSIS
        Generates an RSA or HMAC signature for the header and payload of a JSON Web Token.
    .DESCRIPTION
        Generates an RSA or HMAC signature for the incoming value in the JsonWebToken parameter. The value passed to the JsonWebToken parameter should contain only the header and payload of a JSON Web Token with the appropriate algorithm being defined in the "alg" claim of the header.
    .PARAMETER JsonWebToken
        Contains the JWT minus the signature (encoded header and payload seperated by a period) to generate the HMAC signature for.
    .PARAMETER HashAlgorithm
        The hash algorithim for the signature. Acceptable values are SHA256, SHA384, and SHA512.
    .PARAMETER SigningCertificate
        The certificate containing the private key that will sign the JSON Web Token.
    .PARAMETER Key
        The secret key used to generate the HMAC signature.
    .PARAMETER SkipJwtStructureTest
        Skips testing the incoming JWT for structural validity.
    .EXAMPLE
        $jwtSansSig = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEyMzQ1Njc4OTAsIm5hbWUiOiJKb2huIERvZSIsImFkbWluIjp0cnVlfQ"
        $myCert = Get-Item -Path Cert:\CurrentUser\my\690F0B1D51BD88AEEA1E374B22BBA7BDAB1BE84B
        $rsaSig = New-JwtSignature -JsonWebToken $jwtSansSig -HashAlgorithm SHA256 -SigningCertificate $myCert
        $jws = "{0}.{1}" -f $jwtSansSig, $rsaSig
 
        Produces an SHA256 RSA signature for the JWT in the $jwtSansSig variable and constructs a JSON Web Signature (JWS) defined in the $jws variable.
    .EXAMPLE
        $jwtSansSig = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9"
        $secret = "super secret key!"
        $hmacSig = New-JwtSignature -JsonWebToken $jwtSansSig -Key $secret -HashAlgorithm SHA256
        $jws = "{0}.{1}" -f $jwtSansSig, $hmacSig
 
        Validates the two part passed JWT, produces an HMACSHA256 signature for the JWT in the $jwtSansSig variable, and constructs a JSON Web Signature (JWS) defined in the $jws variable.
    .NOTES
        The resulting HMAC SHA-256, SHA-384, or SHA-512 algorithm is base64url encoded to be compliant with JWT standard RFC 7519.
    .OUTPUTS
        System.String
 
            An digital signature is returned as a base64 URL encoded string.
    .LINK
        https://tools.ietf.org/html/rfc7519
        https://en.wikipedia.org/wiki/RSA_(cryptosystem)
        https://en.wikipedia.org/wiki/HMAC
#>

    [CmdletBinding()]
    [OutputType([System.String])]
    Param (
        [Parameter(Mandatory=$true,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Position=0)]
        [ValidateLength(16,131072)][Alias("JWT", "Token")][String]$JsonWebToken,

        [Parameter(Mandatory=$false,Position=1)]
        [ValidateSet("SHA256","SHA384","SHA512")]
        [String]$HashAlgorithm="SHA256",

        [Parameter(Mandatory=$true,ParameterSetName="RSA",Position=2)][Alias("Certificate", "Cert")]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$SigningCertificate,

        [Parameter(Mandatory = $true, ParameterSetName = "HMAC", Position = 3)]
        [ValidateLength(1, 32768)]
        [String]$Key,

        [Parameter(Mandatory=$false,ValueFromPipeline=$false,Position=4)][Switch]$SkipJwtStructureTest
        )

    BEGIN
    {
        $decodeExceptionMessage = "Unable to decode JWT."
        $ArgumentException = New-Object -TypeName ArgumentException -ArgumentList $decodeExceptionMessage
    }
    PROCESS
    {
        [string]$jwtSignature = ""

        if (-not($PSBoundParameters.ContainsKey("SkipJwtStructureTest")))
        {
            [bool]$isValidJwt = Test-JwtStructure -JsonWebToken $JsonWebToken

            if (-not($isValidJwt))
            {
                Write-Error -Exception $ArgumentException -Category InvalidArgument -ErrorAction Stop
            }
        }

        if ($PSCmdlet.ParameterSetName -eq "HMAC")
        {
            [byte[]]$secretBytes = [Encoding]::ASCII.GetBytes($Key)

            [HMAC]$hmacSha = $null
            switch ($HashAlgorithm)
            {
                "SHA256" { $hmacSha = [HMACSHA256]::new($secretBytes) }
                "SHA384" { $hmacSha = [HMACSHA384]::new($secretBytes) }
                "SHA512" { $hmacSha = [HMACSHA512]::new($secretBytes) }
                default { $hmacSha = [HMACSHA256]::new($secretBytes) }
            }

            $hashBytes = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($JsonWebToken))

            $jwtSignature = ConvertTo-Base64UrlEncodedString -Bytes $hashBytes

            $hmacSha.Dispose()
        }
        else
        {
            try
            {
                $jwtSignature = New-JwtRsaSignature -JsonWebToken $JsonWebToken -SigningCertificate $SigningCertificate -HashAlgorithm $HashAlgorithm
            }
            catch
            {
                $cryptographicExceptionMessage = $_.Exception.Message
                $CryptographicException = New-Object -TypeName System.Security.Cryptography.CryptographicException -ArgumentList $cryptographicExceptionMessage
                Write-Error -Exception $CryptographicException -Category SecurityError -ErrorAction Stop
            }
        }

        return $jwtSignature
    }
}