Functions/Convert-JwkToPem.ps1

function Convert-JwkToPem {
    <#
    .SYNOPSIS
        Converts a JWK public key to a PEM.
    .DESCRIPTION
        Converts JWKs (JSON Web Keys) to formatted PEM (Privacy Enhanced Mail) outputs as well as converting JWKs to unformatted Base64 encoded public keys.
    .PARAMETER Uri
        Specifies the Uniform Resource Identifier (URI) for the JWK set containing the JWKS to convert to PEM format.
    .PARAMETER JsonWebKey
        Specifies an individual JWK string to convert to PEM format.
    .EXAMPLE
        $jwkUri = "https://app.mycompany.com/common/discovery/keys.json"
        Convert-JwkToPem -Uri $jwkUri
 
        Converts the JWKs in the JWK set found in https://app.mycompany.com/common/discovery/keys.json to PEM format.
    .EXAMPLE
        $myJwk = '{
            "kty": "RSA",
            "use": "sig",
            "e": "AQAB",
            "n": "9te1S7Yps5jx1TZtVOo3R_jy157SO-XknHU4WJ5PJ9WdCUXj06PL4HqEgCrXuYLtO8Rl78S1KXD6NRed57fMxB3IhHGEZFuJ_lM6V6l9Y3RqSnOhs0cG_1NohjGeIPbk4u4j4PQDBsbe87qeerBeCsV5hmsQDTC11j_knkkl0cGvhHIcfDkoNS0KyY0LEqCLKaBKxJ7y7oVub5ZR1yHGOzTgFqHY2FcZ9d8wQhc65ngnXQUuI1BplFdEsRAUez5f1_ru3sTQQK7RikH_v2WGltBTQRnfJ4cP3d8SMfcAnKr8QVYdnIFTPocD-k3tkhJjhDpFig8CnN9xca_LWrFhHQ",
            "kid": "jSVgE1Q4he8hiX199BTlS-9YJQE"
        }
        '
        Convert-JwkToPem -JsonWebKey $myJwk | Select -Expand Pem | Out-File .\key.pem -Encoding ascii
 
        Converts the passed JWK into a PEM string and saves it to a file.
    .OUTPUTS
        PSJsonWebToken.PemFromJwkResult
 
            An object containing the JWK ID, PEM and unformatted Base64 public key. .
    .LINK
        https://tools.ietf.org/html/rfc7517
        https://www.rfc-editor.org/rfc/rfc7468
        Select-Object
        Out-File
        New-JsonWebKey
        New-JsonWebKeySet
    #>

    [CmdletBinding()]
    [Alias('cjwk')]
    [OutputType([PSJsonWebToken.PemFromJwkResult])]
    Param (
        [Parameter(Mandatory = $true, ParameterSetName = "URI", Position = 0)][Alias('OidcUri', 'JwkUri')][System.Uri]$Uri,
        [Parameter(Mandatory = $true, ParameterSetName = "JWK", Position = 1)][Alias("jwk")][ValidateLength(12, 1073741791)][String]$JsonWebKey
    )
    BEGIN {
        function _TransformJwkToPem([string]$jwk) {
            try {
                $jwkObject = $jwk | ConvertFrom-Json -ErrorAction Stop

                if (($null -eq $jwkObject.kty) -or ($jwkObject.kty -ne "RSA") -or ($null -eq $jwkObject.n) -or ($null -eq $jwkObject.e)) {
                    $ArgumentException = 'JSON Web Key schema validation failed. Ensure that a valid JWK is passed that contains the key type expressed as "kty", a public exponent as "e”, and modulus as "n" parameters per RFC 7517.'
                    Write-Error -Exception $ArgumentException -ErrorAction Stop
                }

                $rsaParams = [RSAParameters]::new()

                $rsaParams.Exponent = $jwkObject.e | ConvertFrom-Base64UrlEncodedString -AsBytes -ErrorAction Stop
                $rsaParams.Modulus = $jwkObject.n | ConvertFrom-Base64UrlEncodedString -AsBytes -ErrorAction Stop

                $rsaCryptoSp = [RSACryptoServiceProvider]::new()
                $rsaCryptoSp.ImportParameters($rsaParams)

                [byte[]]$publicKeyBytes = $rsaCryptoSp.ExportSubjectPublicKeyInfo()
                [string]$publicKeyUnformatted = ConvertTo-Base64UrlEncodedString -Bytes $publicKeyBytes -ErrorAction Stop
                [string]$publicKeyPem = $rsaCryptoSp.ExportSubjectPublicKeyInfoPem()

                $rsaCryptoSp.Dispose()

                $result = [PSJsonWebToken.PemFromJwkResult]::new()
                $result.JwkIdentifier = $jwkObject.kid
                $result.Pem = $publicKeyPem
                $result.PublicKeyUnformatted = $publicKeyUnformatted

                return $result
            }
            catch {
                $SerializationException = [SerializationException]::new('JSON Web Key schema validation failed. Ensure that a valid JWK is passed that contains the key type expressed as "kty", a public exponent as "e”, and modulus as "n" parameters per RFC 7517.')
                Write-Error -Exception $SerializationException -Category InvalidData -ErrorAction Stop
            }
        }
    }
    PROCESS {
        if ($PsCmdlet.ParameterSetName -eq "URI") {

            $jsonWebKeys = @()
            try {
                $jsonWebKeys += (Get-JwkCollection -Uri $Uri -AsJson -ErrorAction Stop)
            }
            catch {
                Write-Error -Exception $_.Exception -ErrorAction Stop
            }

            foreach ($jwk in $jsonWebKeys) {
                try {
                    $resultingPemObject = _TransformJwkToPem -jwk $jwk
                    Write-Output -InputObject $resultingPemObject
                }
                catch {
                    Write-Error -Exception $_.Exception -ErrorAction Stop
                }
            }
        }
        elseif ($PsCmdlet.ParameterSetName -eq "JWK") {
            try {
                $resultingPemObject = _TransformJwkToPem -jwk $JsonWebKey
                Write-Output -InputObject $resultingPemObject
            }
            catch {
                Write-Error -Exception $_.Exception -ErrorAction Stop
            }
        }
    }
}