Skip to content

Instantly share code, notes, and snippets.

@Nillth
Last active June 14, 2022 07:08
Show Gist options
  • Save Nillth/a50c1f67b9f8cb901c5535e7787891fb to your computer and use it in GitHub Desktop.
Save Nillth/a50c1f67b9f8cb901c5535e7787891fb to your computer and use it in GitHub Desktop.
Create JWT.
<#
.NOTES
===========================================================================
Created on: 2020-11-14
Updated on: 2022-06-14
Created by: Marc Collins
Organization: Qlik Professional Services
Filename: JWTToken.ps1
===========================================================================
#>
function Convert-PemtoX509
{
param
(
[Parameter(Mandatory = $true)]
[System.IO.FileInfo]$PublicKey,
[System.IO.FileInfo]$PrivateKey,
[System.IO.FileInfo]$OutPFX,
[securestring]$PFXPassword,
[switch]$NoPassword
)
BEGIN
{
if ($PFXPassword.Length -eq 0 -and $null -ne $OutPFX -and $NoPassword.IsPresent -eq $false)
{
$PFXPassword = Read-Host -Prompt 'Enter PFX Password' -AsSecureString
}
[string]$KEY_HEADER = '-----BEGIN RSA PRIVATE KEY-----'
[string]$KEY_FOOTER = '-----END RSA PRIVATE KEY-----';
[System.Security.Cryptography.X509Certificates.X509Certificate2]$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
function Get-RSA
{
param
(
[Parameter(Mandatory = $false)]
[string]$PEM
)
if (Test-IsRSAPrivateKeyAvailable -PEM $PEM)
{
[System.Security.Cryptography.RSAParameters]$PrivateKey = Convert-PEMToRSAParameters -PEM $PEM
[System.Security.Principal.SecurityIdentifier]$everyoneSI = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::WorldSid, $null)
[System.Security.AccessControl.CryptoKeyAccessRule]$rule = New-Object System.Security.AccessControl.CryptoKeyAccessRule($everyoneSI, [System.Security.AccessControl.CryptoKeyRights]::FullControl, [System.Security.AccessControl.AccessControlType]::Allow)
[System.Security.Cryptography.CspParameters]$cspParameters = New-Object System.Security.Cryptography.CspParameters;
$cspParameters.KeyContainerName = 'MY_C_NAME';
$cspParameters.ProviderName = 'Microsoft Strong Cryptographic Provider';
$cspParameters.ProviderType = 1;
$cspParameters.Flags = [System.Security.Cryptography.CspProviderFlags]::UseMachineKeyStore;
$cspParameters.CryptoKeySecurity = New-Object System.Security.AccessControl.CryptoKeySecurity
$cspParameters.CryptoKeySecurity.SetAccessRule($rule);
[System.Security.Cryptography.RSACryptoServiceProvider]$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider($cspParameters);
$rsa.PersistKeyInCsp = $true;
$rsa.ImportParameters($privateKey);
}
return $rsa;
}
function Convert-PEMToRSAParameters
{
param
(
[Parameter(Mandatory = $true)]
[string]$PEM
)
if (Test-IsRSAPrivateKeyAvailable -PEM $PEM)
{
$keyFormatted = $PEM
[int]$cutIndex = $keyFormatted.IndexOf($KEY_HEADER);
$keyFormatted = $keyFormatted.Substring($cutIndex, $keyFormatted.Length - $cutIndex);
$cutIndex = $keyFormatted.IndexOf($KEY_FOOTER);
$keyFormatted = $keyFormatted.Substring(0, $cutIndex + $KEY_FOOTER.Length);
$keyFormatted = $keyFormatted.Replace($KEY_HEADER, '');
$keyFormatted = $keyFormatted.Replace($KEY_FOOTER, '');
$keyFormatted = $keyFormatted.Replace('\r', '');
$keyFormatted = $keyFormatted.Replace('\n', '');
$keyFormatted = $keyFormatted.Trim();
[byte[]]$privateKeyInDER = [System.Convert]::FromBase64String($keyFormatted);
$MemoryStream = New-Object System.IO.MemoryStream( , $privateKeyInDER)
$BinaryReader = New-Object System.IO.BinaryReader( , $MemoryStream)
[System.UInt16]$twobytes = 0;
[int]$elements = 0;
[byte]$bt = 0;
try
{
$twobytes = $binaryReader.ReadUInt16();
if ($twobytes -eq 0x8130)
{
$binaryReader.ReadByte() | Out-Null
}
elseif ($twobytes -eq 0x8230)
{
$binaryReader.ReadInt16() | Out-Null
}
else
{
throw 'Wrong data';
}
$twobytes = $binaryReader.ReadUInt16();
if ($twobytes -ne 0x0102)
{
throw 'Wrong data';
}
$bt = $binaryReader.ReadByte();
if ($bt -ne 0x00)
{
throw 'Wrong data'
}
$elements = Get-IntegerSize($binaryReader)
$paramModulus = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramE = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramD = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramP = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramQ = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramDP = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramDQ = $binaryReader.ReadBytes($elements);
$elements = Get-IntegerSize($binaryReader)
$paramIQ = $binaryReader.ReadBytes($elements);
Assert-Length -Data ([ref]$paramD) -DesiredLength 256
Assert-Length -Data ([ref]$paramDP) -DesiredLength 128
Assert-Length -Data ([ref]$paramDQ) -DesiredLength 128
Assert-Length -Data ([ref]$paramE) -DesiredLength 3
Assert-Length -Data ([ref]$paramIQ) -DesiredLength 128
Assert-Length -Data ([ref]$paramModulus) -DesiredLength 256
Assert-Length -Data ([ref]$paramP) -DesiredLength 128
Assert-Length -Data ([ref]$paramQ) -DesiredLength 128
[System.Security.Cryptography.RSAParameters]$rsaParameters = New-Object System.Security.Cryptography.RSAParameters
$rsaParameters.Modulus = $paramModulus;
$rsaParameters.Exponent = $paramE;
$rsaParameters.D = $paramD;
$rsaParameters.P = $paramP;
$rsaParameters.Q = $paramQ;
$rsaParameters.DP = $paramDP;
$rsaParameters.DQ = $paramDQ;
$rsaParameters.InverseQ = $paramIQ;
return $rsaParameters;
}
finally
{
$BinaryReader.Close() | Out-Null
}
}
}
function Get-IntegerSize
{
param
(
[Parameter(Mandatory = $true)]
[System.IO.BinaryReader]$Binary
)
[byte]$bt = 0;
[byte]$lowbyte = 0x00;
[byte]$highbyte = 0x00;
[int]$count = 0;
$bt = $binary.ReadByte();
if ($bt -ne 0x02)
{
return 0
}
$bt = $binary.ReadByte();
if ($bt -eq 0x81)
{
$count = $binary.ReadByte()
}
elseif ($bt -eq 0x82)
{
$highbyte = $binary.ReadByte();
$lowbyte = $binary.ReadByte();
[byte[]]$modint = $lowbyte, $highbyte, 0x00, 0x00;
$count = [System.BitConverter]::ToInt32($modint, 0);
}
else
{
$count = $bt;
}
while ($binary.ReadByte() -eq 0x00)
{
$count -= 1;
}
$binary.BaseStream.Seek(-1, [System.IO.SeekOrigin]::Current) | Out-Null
return $count;
}
function Assert-Length
{
param
(
[Parameter(Mandatory = $true)]
[ref]$Data,
[Parameter(Mandatory = $true)]
[int]$DesiredLength
)
if ($null -eq $data -or $data.Value.Length -ge $desiredLength)
{
return;
}
[int]$zeros = $desiredLength - $data.Value.Length;
[byte[]]$newData = [byte[]]::new($desiredLength)
[System.Array]::Copy($data.Value, 0, $newData, $zeros, $data.Value.Length);
$data.Value = $newData;
}
function Test-IsRSAPrivateKeyAvailable
{
param
(
[Parameter(Mandatory = $false)]
[string]$PEM
)
return (($null -ne $PEM) -and $PEM.Contains($KEY_HEADER) -and $PEM.Contains($KEY_FOOTER))
}
}
PROCESS
{
if ($PublicKey.Exists)
{
[string]$PEMPublicKey = [System.IO.File]::ReadAllText($PublicKey)
}
if ($PrivateKey.Exists)
{
[string]$PEMPrivateKey = [System.IO.File]::ReadAllText($PrivateKey)
}
[byte[]]$pemCertWithPrivateKey = [System.Text.Encoding]::ASCII.GetBytes($PEMPublicKey)
[System.Security.Cryptography.RSACryptoServiceProvider]$rsaPK = Get-RSA -PEM $PEMPrivateKey
$cert.Import($pemCertWithPrivateKey, '', ([System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::MachineKeySet, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable))
if ($null -ne $rsaPK)
{
$cert.PrivateKey = $rsaPK;
}
}
END
{
if ($null -ne $OutPFX)
{
if ($PFXPassword.Length -gt 0 -and $NoPassword.IsPresent -eq $false)
{
[system.io.file]::WriteAllBytes($OutPFX.fullname, $Cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx, $PFXPassword))
}
else
{
[system.io.file]::WriteAllBytes($OutPFX.fullname, $Cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx))
}
}
else
{
return $cert;
}
}
}
function New-JWTToken
{
[CmdletBinding(DefaultParameterSetName = 'DefaultPEM')]
param
(
[Parameter(ParameterSetName = 'DefaultPEM',
Mandatory = $true)]
[Parameter(ParameterSetName = 'DefaultX509',
Mandatory = $true)]
[string]$UserID,
[Parameter(ParameterSetName = 'DefaultPEM',
Mandatory = $true)]
[Parameter(ParameterSetName = 'DefaultX509',
Mandatory = $true)]
[string]$UserDirectory,
[Parameter(ParameterSetName = 'DefaultPEM',
Mandatory = $false)]
[Parameter(ParameterSetName = 'DefaultX509',
Mandatory = $false)]
[string]$Roles,
[Parameter(ParameterSetName = 'DefaultPEM',
Mandatory = $false)]
[Parameter(ParameterSetName = 'DefaultX509',
Mandatory = $false)]
[Hashtable]$Attributes,
[Parameter(ParameterSetName = 'PayloadPEM',
Mandatory = $true)]
[Parameter(ParameterSetName = 'PayloadX509',
Mandatory = $true)]
[Hashtable]$Payload,
[Parameter(ParameterSetName = 'PayloadPEM',
Mandatory = $true)]
[Parameter(ParameterSetName = 'DefaultPEM',
Mandatory = $true)]
[system.IO.FileInfo]$PublicKeyPEM,
[Parameter(ParameterSetName = 'PayloadPEM',
Mandatory = $true)]
[Parameter(ParameterSetName = 'DefaultPEM',
Mandatory = $true)]
[system.IO.FileInfo]$PrivateKeyPEM,
[Parameter(ParameterSetName = 'PayloadX509',
Mandatory = $true)]
[Parameter(ParameterSetName = 'DefaultX509',
Mandatory = $true)]
[System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
[datetime]$Expiry,
[hashtable]$AdditionalHeader
)
BEGIN
{
Add-Type -AssemblyName System.IdentityModel
if ($null -eq $Certificate)
{
$Certificate = Convert-PemtoX509 -PublicKey $PublicKeyPEM.FullName -PrivateKey $PrivateKeyPEM.FullName
}
Write-Verbose $Certificate.Thumbprint
$X509SigningCredentials = New-Object System.IdentityModel.Tokens.X509SigningCredentials($Certificate)
$SignatureFormatter = $X509SigningCredentials.SigningKey.GetSignatureFormatter($X509SigningCredentials.SignatureAlgorithm)
$HashAlgorithm = [System.Security.Cryptography.HashAlgorithm]::Create([System.Security.Cryptography.HashAlgorithmName]::SHA256)
$Algorithm = 'RS256'
$type = 'JWT'
if ($PSBoundParameters.ContainsKey('AdditionalHeader'))
{
$AdditionalHeader.alg = $Algorithm;
$AdditionalHeader.typ = $type
$headerjson = $AdditionalHeader | ConvertTo-Json -Compress
}
else
{
[hashtable]$header = @{
alg = $Algorithm; typ = $type
}
$headerjson = $header | ConvertTo-Json -Compress
}
Write-Verbose $headerjson
}
PROCESS
{
$JWT = $Null
if ($PSBoundParameters.ContainsKey('Expiry'))
{
$exp = [int][double]::parse((Get-Date -Date $($Expiry.ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp and add desired expiration.
}
else
{
$ValidforSeconds = 240
$exp = [int][double]::parse((Get-Date -Date $((Get-Date).addseconds($ValidforSeconds).ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp and add desired expiration.
}
if ($PSCmdlet.ParameterSetName -in 'DefaultPEM', 'DefaultX509')
{
[hashtable]$Payload = @{
'UserId' = "$UserID"
'UserDirectory' = "$UserDirectory"
'exp' = $exp
}
if (![string]::IsNullOrEmpty($Attributes))
{
$Payload.Attributes = $Attributes
}
if (![string]::IsNullOrEmpty($Roles))
{
$Payload.roles = $Roles
}
}
else
{
#Add the Exp to the PayLoad Hashtable
$Payload.exp = $exp
}
$JsonPayload = $payload | ConvertTo-Json -Compress
Write-Verbose $JsonPayload
$headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)).Split('=')[0].Replace('+', '-').Replace('/', '_')
$payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($JsonPayload)).Split('=')[0].Replace('+', '-').Replace('/', '_')
$ToBeSigned = "$($headerjsonbase64).$($payloadjsonbase64)"
$BytesToBeSigned = ([System.Text.Encoding]::UTF8).GetBytes($ToBeSigned)
$ToBeSignedHash = $HashAlgorithm.ComputeHash($BytesToBeSigned)
$Sig = [Convert]::ToBase64String($SignatureFormatter.CreateSignature($ToBeSignedHash)) -replace '\+', '-' -replace '/', '_' -replace '='
$JWT = "$($ToBeSigned).$($sig)"
return $JWT
}
}
<# # Examples
#Using PEMs with UserID & UserDirectory
$PublicKeyFile = "C:\Path\To\PublicKey.pem"
$PrivatekeyFile = "C:\Path\To\PrivateKey.pem"
New-JWTToken -PublicKeyPEM $PublicKeyFile -PrivateKeyPEM $PrivatekeyFile -UserID "MarcTestxxx" -UserDirectory "JWTTest"
#Using X509Cert with UserID & UserDirectory
$Cert = Get-ChildItem Cert:\CurrentUser\My\6FB985D49DA34EAF58B47BE9F4D9BFB482F5D211
New-JWTToken -Certificate $Cert -UserID "MarcTestxxx" -UserDirectory "SSS"
#Using Custom Payload
$Payload = @{
CustomUID = "UserID"
CustomUD = "UserDirectory"
OtherAttributes = @{
as = "many"
asyou = "need"
}
OrWhatever = "data you want"
email = "marc.collins@qlik.com"
}
New-JWTToken -Certificate $Cert -Payload $Payload
New-JWTToken -PublicKeyPEM $PublicKeyFile -PrivateKeyPEM $PrivatekeyFile -Payload $Payload
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment