Generate HMAC SHA256 signature Powershell - powershell

For 3commas I tried to generate a HMAC SHA256 signature in Powershell with the example parameters from the documentation:
$secret = 'NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j'
$message = '/public/api/ver1/accounts/new?type=binance&name=binance_account&api_key=XXXXXX&secret=YYYYYY'
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($signature)
$signature
It generates "MPZ4oVcjApDgBHXP/8y8kq42WdlMFFosDp0Poo9BwRo="
As described in the documentation it should generate "30f678a157230290e00475cfffccbc92ae3659d94c145a2c0e9d0fa28f41c11a"
[linux]$ echo -n "/public/api/ver1/accounts/new?type=binance&name=binance_account&api_key=XXXXXX&secret=YYYYYY" | openssl dgst -sha256 -hmac "NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j"
(stdin)= 30f678a157230290e00475cfffccbc92ae3659d94c145a2c0e9d0fa28f41c11a
Can anyone help me out?

Use the BitConverter class.
$secret = 'NhqPtmdSJYdKjVHjA7PZj4Mge3R5YNiP1e3UZjInClVN65XAbvqqM6A7H5fATj0j'
$message = '/public/api/ver1/accounts/new?type=binance&name=binance_account&api_key=XXXXXX&secret=YYYYYY'
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature1 = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
#$signature2 = [Convert]::ToBase64String($signature1)
[System.BitConverter]::ToString($signature1) -replace '-', ''

Related

Service bus queue message Content convert to string with PowerShell

I use rest api with powershell to get details from the Service Bus queue message. I am not sure when it happened, but now Content is in bytes ex: Content:{64, 6, 115, 116…}.
How can I convert it to the normal string with data?
function Get-SAStoken {
param (
$QueueName,
$Access_Policy_Name,
$Access_Policy_Key
)
$expireInSeconds = 300
[Reflection.Assembly]::LoadWithPartialName("System.Web")| out-null
$uri="my.servicebus.windows.net/$QueueName"
#Token expires now+300
$expires=([DateTimeOffset]::Now.ToUnixTimeSeconds())+ $expireInSeconds
$signatureString=[System.Web.HttpUtility]::UrlEncode($uri)+ "`n" + [string]$expires
$HMAC = New-Object System.Security.Cryptography.HMACSHA256
$HMAC.key = [Text.Encoding]::ASCII.GetBytes($Access_Policy_Key)
$signature = $HMAC.ComputeHash([Text.Encoding]::ASCII.GetBytes($signatureString))
$signature = [Convert]::ToBase64String($signature)
$sasToken = "SharedAccessSignature sr={0}&sig={1}&se={2}&skn={3}" -f [System.Web.HttpUtility]::UrlEncode($uri),
[System.Web.HttpUtility]::UrlEncode($signature),
[System.Web.HttpUtility]::UrlEncode($expires),
[System.Web.HttpUtility]::UrlEncode($Access_Policy_Name)
return $sasToken
}
function Get-SBmessage {
param (
$SASToken,
$Queue
)
$queue = $Queue
$header = #{ Authorization = $SASToken }
$postService = Invoke-WebRequest -Uri "https://my.servicebus.windows.net/$queue/messages/head" `
-Headers $header `
-Method Post
return $postService
}
$Queue = "capacity-checker"
$SAStokenRunningTest = Get-SAStoken -QueueName $Queue -Access_Policy_Name "pipeline" -Access_Policy_Key "key-for-sb-queue"
$SBmessage = Get-SBmessage -SASToken $SAStokenRunningTest -Queue $Queue
$SBmessage
So my solution is
[byte[]]$bytes = $SBmessage.Content
$msContent = [System.Text.Encoding]::ASCII.GetString($bytes)
Thanks, #MathiasR.Jessen for the help

JSON Web Token, signature not matching using powershell and Azure AD app

I'm creating a solution to get info from our Azure AD tenant. I'm using powershell Invoke-Webrequest and OAuth 2.0 client credential grant to get the info. When I use a client secret my script is working fine but I want to make it more secure and use a self-signed certificate.
I use the following command to create a new certificate on my local pc:
$cert = New-SelfSignedCertificate -HashAlgorithm "SHA256" -Subject "CN=******" -CertStoreLocation "Cert:\Currentuser\My" -KeyExportPolicy Exportable -KeySpec Signature -NotAfter (Get-Date).AddYears(5)
After creating I export this certificate to .cer using mmc and upload it to the Azure AD app.
Image from uploaded certificate
I also creating a JSON Web Token using the following script:
############################################
## Variable
############################################
$appEndPoint = "https://login.microsoftonline.com/************/oauth2/token"
$appClientID = "*******************************"
$exportPath = [Environment]::GetFolderPath("desktop")
$guid = [guid]::NewGuid()
############################################
## JWT Token starttime/endtime
############################################
$jwtStartTimeUnix = ([DateTimeOffset](Get-Date).ToUniversalTime()).ToUnixTimeSeconds()
$jwtEndTimeUnix = ([DateTimeOffset](Get-Date).AddHours(1).ToUniversalTime()).ToUnixTimeSeconds()
###########################################
##Decoded Json JWT Token
###########################################
$jwtID = $guid.Guid
$decJwtHeader = '{"alg":"RS256","typ":"JWT","x5t":"' + [System.Convert]::ToBase64String(($cert.GetCertHash())) + '"}'
#$decJwtPayLoad = '{"ver":"2.0","aud":"'+ $appEndPoint + '","exp":' + $jwtEndTimeUnix + ',"iss":"' + $appClientID + '","jti":"' + $jwtID + '","nbf":' + $jwtStartTimeUnix + ',"sub":"' + $appClientID +'"}'
$decJwtPayLoad = '{
"aud":"' + $appEndPoint + '"
, "exp":"' + $jwtEndTimeUnix + '"
, "iss":"' + $appClientID + '"
, "jti":"' + $jwtID + '"
, "nbf":"' + $jwtStartTimeUnix + '"
, "sub":"' + $appClientID + '"
}'
##########################################
##Encode Json JWT Token
##########################################
$encJwtHeaderBytes = [system.text.encoding]::UTF8.GetBytes($decJwtHeader)
$encHeader = [system.convert]::ToBase64String($encJwtHeaderBytes).Split('=')[0].Replace('+', '-').Replace('/', '_')
$encJwtPayLoadBytes = [system.text.encoding]::UTF8.GetBytes($decJwtPayLoad)
$encPayLoad = [system.convert]::ToBase64String($encJwtPayLoadBytes).Split('=')[0].Replace('+', '-').Replace('/', '_')
$jwtToken = $encHeader + '.' + $encPayLoad
$toSign = [system.text.encoding]::UTF8.GetBytes($jwtToken)
#########################################
##Sign JWT Token
#########################################
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = "2.16.840.1.101.3.4.2.1"
$dataBytes = [System.Text.Encoding]::UTF8.GetBytes($toSign)
$hashBytes = $HashAlgo.ComputeHash($dataBytes)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
$sig = [System.Convert]::ToBase64String($signedBytes) -replace '\+','-' -replace '/','_' -replace '='
#$sig = [system.convert]::ToBase64String($cert.PrivateKey.SignData($toSign,[Security.Cryptography.HashAlgorithmName]::SHA256,[Security.Cryptography.RSASignaturePadding]::Pkcs1)) -replace '\+','-' -replace '/','_' -replace '='
$jwtToken = $jwtToken + '.' + $sig
$exportPath = $exportPath + "\jwtToken.txt"
$jwtToken | Out-File $exportPath
This JSON Web Token is the client assertion in my Invoke-Webrequest
#################################################################
## ENKEL VOOR TESTING, NIET IN AMP GEBRUIKEN ####################
#################################################################
$tenantid = '**********'
$subscriptionid = '**********'
$clientid = '**********'
$client_assertion_type = 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'
$client_assertion = '********'
##################################################################
##################################################################
##################################################################
$return = Invoke-Command -ScriptBlock {
param($tenantid,$subscriptionid,$clientid,$client_assertion_type,$client_assertion)
Add-Type -AssemblyName System.Web
$enc_client_assertion_type = [System.Web.HttpUtility]::UrlEncode($client_assertion_type)
$encScope = [System.Web.HttpUtility]::UrlEncode('https://management.azure.com/.default')
$enc_client_assertion = [System.Web.HttpUtility]::UrlEncode($client_assertion)
$body = "scope=$encScope&client_id=$clientid&client_assertion_type=$enc_client_assertion_type&client_assertion=$enc_client_assertion&grant_type=client_credentials"
$auth = Invoke-WebRequest "https://login.microsoftonline.com/$tenantid/oauth2/token" -Method Post -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing
$token = ($auth | ConvertFrom-Json).access_token
$headers = #{
'Authorization'="Bearer $($token)"
}
$data = Invoke-WebRequest "https://management.azure.com/subscriptions/$subscriptionid/providers/Microsoft.Advisor/recommendations?api-version=2017-04-19" -Method GET -Headers $headers -UseBasicParsing
New-Object PSObject -Property #{
content=$data.content
}
} -ArgumentList $tenantid,$subscriptionid,$clientid,$client_assertion_type,$client_assertion
$content = $return.content
Write-Host $content
I'm receiving the following error but can't find out why. Anyone an idea?
Invoke-WebRequest : {"error":"invalid_client","error_description":"AADSTS700027: Client assertion contains an invalid signature. [Reason - The provided signature value did not match the expected signature value.,
Thumbprint of key used by client: '******************', Found key 'Start=10/14/2019 09:02:58, End=10/14/2024 09:12:59', Please visit 'https://developer.microsoft.com/en-us/graph/graph-explorer'
and query for 'https://graph.microsoft.com/beta/applications/***********' to see configured keys]\r\nTrace ID: 085758a5-7470-4a3d-91ba-6f98518e7100\r\nCorrelation ID:
1ab6c7d2-4e46-4e7b-a56f-42720a24286a\r\nTimestamp: 2019-10-14 09:43:15Z","error_codes":[700027],"timestamp":"2019-10-14
09:43:15Z","trace_id":"085758a5-7470-4a3d-91ba-6f98518e7100","correlation_id":"1ab6c7d2-4e46-4e7b-a56f-42720a24286a","error_uri":"https://login.microsoftonline.com/error?code=700027"}
You are almost right!
The only problem is that you forget using your own certificate:
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = "2.16.840.1.101.3.4.2.1"
$dataBytes = [System.Text.Encoding]::UTF8.GetBytes($toSign)
$hashBytes = $HashAlgo.ComputeHash($dataBytes)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
The right way is that:
#Get cert from your cert store. Please make sure that the private key is exportable
$Cert = Get-ChildItem -Path cert:\Currentuser\My | Where {$_.Subject -eq "CN=AADApplicationWithCert"}
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256");
#Use your private key
$RSACryptoSP.FromXmlString($Cert.PrivateKey.ToXmlString($true))
$hashBytes = $HashAlgo.ComputeHash($toSign)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
$signedBytes = [Convert]::ToBase64String($signedBytes) -replace '\+','-' -replace '/','_' -replace '='
My whole sample:
$appEndPoint = "https://login.microsoftonline.com/hanxia.onmicrosoft.com/oauth2/token"
$appClientID = "dc175b96-c196-43cf-aa0b-ea03e56da5e7"
$jwtStartTimeUnix = ([DateTimeOffset](Get-Date).ToUniversalTime()).ToUnixTimeSeconds()
$jwtEndTimeUnix = ([DateTimeOffset](Get-Date).AddHours(1).ToUniversalTime()).ToUnixTimeSeconds()
$jwtID = [guid]::NewGuid().Guid
$Cert = Get-ChildItem -Path cert:\Currentuser\My | Where {$_.Subject -eq "CN=AADApplicationWithCert"}
$decJwtHeader = #{
alg="RS256";
typ="JWT";
x5t=[System.Convert]::ToBase64String($Cert.GetCertHash())
} | ConvertTo-Json -Compress
$decJwtPayLoad = #{
aud = $appEndPoint;
exp = $jwtEndTimeUnix;
iss = $appClientID;
jti = $jwtID;
nbf = $jwtStartTimeUnix;
sub = $appClientID
} | ConvertTo-Json -Compress
$encJwtHeaderBytes = [system.text.encoding]::UTF8.GetBytes($decJwtHeader)
$encHeader = [system.convert]::ToBase64String($encJwtHeaderBytes) -replace '\+','-' -replace '/','_' -replace '='
$encJwtPayLoadBytes = [system.text.encoding]::UTF8.GetBytes($decJwtPayLoad)
$encPayLoad = [system.convert]::ToBase64String($encJwtPayLoadBytes) -replace '\+','-' -replace '/','_' -replace '='
$jwtToken = $encHeader + '.' + $encPayLoad
$toSign = [system.text.encoding]::UTF8.GetBytes($jwtToken)
$RSACryptoSP = [System.Security.Cryptography.RSACryptoServiceProvider]::new()
$HashAlgo = [System.Security.Cryptography.SHA256CryptoServiceProvider]::new()
$sha256oid = [System.Security.Cryptography.CryptoConfig]::MapNameToOID("SHA256");
$RSACryptoSP.FromXmlString($Cert.PrivateKey.ToXmlString($true))
$hashBytes = $HashAlgo.ComputeHash($toSign)
$signedBytes = $RSACryptoSP.SignHash($hashBytes, $sha256oid)
$sig = [Convert]::ToBase64String($signedBytes) -replace '\+','-' -replace '/','_' -replace '='
$jwtToken = $jwtToken + '.' + $sig
$jwtToken

HMAC SHA256 Powershell convert

Why does the following powershell script:
$privateKey = "843c1f887b"
$requestData = "1543448572|d.lastname#firm.com|Firstname Lastname"
function signRequest {
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($privateKey)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($requestData))
$signature = [Convert]::ToBase64String($signature)
$outi = $signature
return $signature
}
convert to hash:
FipK51tOtzb2m2yFQAf5IK6BNthClnqE24luMzYMPuo=
and other online hmac sha256 generators with the same input to:
162a4ae75b4eb736f69b6c854007f920ae8136d842967a84db896e33360c3eea
any suggestions what I'm doing wrong in the script?
Thanks!
Your code produces the correct HMAC, you're just base64-encoding it instead of output a hex string like all the other tools.
Change this line
$signature = [Convert]::ToBase64String($signature)
to
$signature = [System.BitConverter]::ToString($signature).Replace('-','').ToLower()
Explanation:
[BitConverter]::ToString($signature) produces a hexadecimal string (16-2A-4A-E7...)
String.Replace('-','') removes the - (162A4AE7...)
String.ToLower() lowercases the final string (162a4ae7...)

Using PowerShell to Create Self-Signed Certificate of 2048 bits length

I use the function bellow to generate self-signed certificate for using it into IIS.
My question is how to generate the certificate with the public key of length 2048 bits?
I have changed $key.Length to 2048 but when I generate the certificate, public key is only 1024 bits.
function Add-SelfSignedCertificate
{
[CmdletBinding()]
param
(
[Parameter(Mandatory=$True, ValueFromPipelineByPropertyName=$True)]
[Alias('cn')]
[string]$CommonName
)
$name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
$name.Encode("CN=$CommonName", 0)
$key = new-object -com "X509Enrollment.CX509PrivateKey.1"
$key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
$key.KeySpec = 1
$key.Length = 2048
$key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
$key.MachineContext = 1
$key.Create()
$serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
$serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1")
$ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
$ekuoids.add($serverauthoid)
$ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
$ekuext.InitializeEncode($ekuoids)
$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
$cert.InitializeFromPrivateKey(2, $key, "")
$cert.Subject = $name
$cert.Issuer = $cert.Subject
$cert.NotBefore = get-date
$cert.NotAfter = $cert.NotBefore.AddYears(5)
$cert.X509Extensions.Add($ekuext)
$cert.Encode()
$enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
$enrollment.InitializeFromRequest($cert)
$enrollment.CertificateFriendlyName = $CommonName
$certdata = $enrollment.CreateRequest(0)
$enrollment.InstallResponse(2, $certdata, 0, "")
}
Why don't you generate a 2048 first and then add it like :
New-SelfSignedCertificate -Type Custom -Subject "E=a.b#test.com,CN=user" -TextExtension #("2.5.29.37={text}1.3.6.1.5.5.7.3.4","2.5.29.17={text}email=a.b#test.com&upn=ab#test.com") -KeyUsage DataEncipherment -KeyAlgorithm RSA -KeyLength 2048 -SmimeCapabilities -CertStoreLocation "Cert:\CurrentUser\My"
Then after this you can add it.
Hope it helps.
After restarting the machine the problem was solved, I ran the script and it worked fine.

Encoding AWS Affiliate API String with PS gives wrong hash

From the following guide in AWS I need to generate an HMAC SHA256 Signature:
http://docs.aws.amazon.com/AWSECommerceService/latest/DG/rest-signature.html
My code is as follows:
$message = 'GET
webservices.amazon.com
/onca/xml
AWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews&Service=AWSECommerceService&Timestamp=2014-08-18T12%3A00%3A00Z&Version=2013-08-01'
$secret = '1234567890'
$hmacsha = New-Object System.Security.Cryptography.HMACSHA256
$hmacsha.key = [Text.Encoding]::ASCII.GetBytes($secret)
$signature = $hmacsha.ComputeHash([Text.Encoding]::ASCII.GetBytes($message))
$signature = [Convert]::ToBase64String($signature)
echo $signature
# Do we get the expected signature?
echo ($signature -eq 'j7bZM0LXZ9eXeZruTqWm2DIvDYVUU3wxPPpp+iXxzQc=')
Unfortunately I do not get the same hash as AWS does in it's example. What did I miss?
I got it eventually.
It had something to do with the line breaks:
$message = "GET`nwebservices.amazon.com`n/onca/xml`nAWSAccessKeyId=AKIAIOSFODNN7EXAMPLE&AssociateTag=mytag-20&ItemId=0679722769&Operation=ItemLookup&ResponseGroup=Images%2CItemAttributes%2COffers%2CReviews&Service=AWSECommerceService&Timestamp=2014-08-18T12%3A00%3A00Z&Version=2013-08-01"