Powershell equivalent of 'openssl dgst -sha512 -sign' - powershell

For API access, I need to sign a JSON file with 'openssl dgst -sha512 -sign'. I need to run this in a powershell script.
Currently, I am using the following method:
Write the file to a temp file, and re-read it to be certain that there is no conversion problem
Invoke openssl to sign the file
Read the signature file
Call the API
$body = #{
"login" = "customer"
"nonce" = Get-Random -Minimum ([int]([math]::pow(10,8))) -Maximum ([int]([math]::pow(10,9)-1))
"read_only" = "true"
"expiration_time" = "30 seconds"
"label" = "Test"
"global_key" = "true"
} | ConvertTo-Json
Remove-Item -Force "D:\temp\body.tmp" -ErrorAction Continue
Remove-Item -Force "D:\temp\body.dgst" -ErrorAction Continue
$body | Out-File -FilePath "D:\temp\body.tmp" -Encoding ascii
# Re-read data from the file after it's been re-encoded to ascii (as thats what we signed)
$body = [System.IO.File]::ReadAllBytes("D:\temp\body.tmp")
# Sign with the private key of the signing certificate
& D:\apps\openssl\bin\openssl.exe dgst -sha512 -sign "D:\powershell\TransIP\sign.txt" -out "D:\temp\body.dgst" "D:\temp\body.tmp"
$headers = #{
Signature = ([Convert]::ToBase64String([System.IO.File]::ReadAllBytes("D:\temp\body.dgst")))
"Content-Type" = "application/json"
}
I've been searching for a native Powershell/.NET method for SHA512 digest signing, but I can't seem to find anything in the Security class to do this.
Does anyone know a native Powershell method (thus eliminating the dependency of an external openssl installation), or is the above method the only method for signing?
Documentation of the API that I'm trying to use: https://api.transip.nl/rest/docs.html (section 'Authentication'). I've already asked TransIP for support, but they don't support Powershell.
Documentation of 'openssl dgst': https://www.openssl.org/docs/man1.1.1/man1/dgst.html
Joost

Related

How to get the fingerprint of a base64 encoded pfx certificate in windows powershell

I have a base64 encoded .pfx certificate that was encoded in linux using:
cat $my_pfx_cert | base64 -w 0
In windows Powershell, I'm unable to decode this string and produce a meaningful fingerprint however. Below is is my attempt...
$output = "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"
$converted = $([System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($output)))
$converted | Out-File -FilePath "$HOME\.ssh\Deadline10RemoteClient_from_bash.pfx"
$cert_password = ""
New-Object System.Security.Cryptography.X509Certificates.X509Certificate2("$HOME\.ssh\Deadline10RemoteClient_from_bash.pfx", $cert_password)
The equivalent in bash however does work:
output="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"
echo "$output" | base64 --decode > "$HOME/.ssh/Deadline10RemoteClient_from_bash.pfx"
openssl pkcs12 -in "$HOME/.ssh/Deadline10RemoteClient_from_bash.pfx" -nodes -passin pass: |openssl x509 -noout -fingerprint
You're likely messing up the encoding when converting it to a string and writing to file with Out-File, so skip that part entirely - the X509Certificate2 constructor takes a raw byte array as input anyway:
$output = "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"
$rawCertBytes = [Convert]::FromBase64String($output)
$certificate = [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($rawCertBytes)
Now that you have a [X509Certificate2] object, either read the thumbprint via the API:
$certificate.Thumbprint
Or, if you want to analyze the certificate file on disk (with openssl for example), export it:
# Create a new file
$certFile = New-Item certificate.cer -ItemType File
# Open a writable file stream
$fileStream = $certFile.OpenWrite()
try {
# Write cert bytes to disk
$certBytes = $certificate.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Cert)
$fileStream.Write($certBytes, 0, $certBytes.Length)
$fileStream.Flush($true)
$fileStream.Close()
}
finally {
# Clean up open file stream
$fileStream |ForEach-Object Dispose
}

Compile Powershell script into interpreted code

Please pardon me if I have misstated the title, but here is what I am looking for:
Let's say I have this script:
$secureCiphers = #(
'AES 128/128',
'AES 256/256'
)
foreach ($secureCipher in $secureCiphers) {
$key = (Get-Item HKLM:\).OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey($secureCipher)
New-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\$secureCipher" -name 'Enabled' -value '0xffffffff' -PropertyType 'DWord' -Force | Out-Null
$key.close()
Write-Host "Strong cipher $secureCipher has been enabled."
}
Is there something out there will unroll the loop, make assignments for each iteration and say the above code is equivalent to:
$key = (Get-Item HKLM:\).OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('AES 128/128')
New-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\AES 128/128" -name 'Enabled' -value '0xffffffff' -PropertyType 'DWord' -Force | Out-Null
$key.close()
Write-Host "Strong cipher AES 128/128 has been enabled."
$key = (Get-Item HKLM:\).OpenSubKey('SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers', $true).CreateSubKey('AES 256/256')
New-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Ciphers\AES 256/256" -name 'Enabled' -value '0xffffffff' -PropertyType 'DWord' -Force | Out-Null
$key.close()
Write-Host "Strong cipher AES 256/256 has been enabled."
I can then write an automated test to say the script was supposed to set these registry keys and the system has the expected values for those registry keys. Does this request make sense? The scripts are more complicated and have functions and a lot of if-else statements.
I think instead what might serve you better is to abstract the logic of what you're trying to do into functions (preferably in a module), which take parameters for the things that change. So in this case, you'd have a function that takes a particular "secure cipher" and does what it needs to do.
Once you've got the logic in a function, you can write tests around the function, that ensure it works as expected, that it fails as expected when passed invalid entries, etc.
PowerShell has a testing framework called Pester that is helpful for writing tests.
Now you've got re-usable function, with tests to prove it works.
Your script should then become:
$secureCiphers = #(
'AES 128/128',
'AES 256/256'
)
foreach ($secureCipher in $secureCiphers) {
Set-MySecureCipher -Cipher $secureCipher
}
Or, if you wrote your function to be able to accept pipeline input, it may be:
$secureCiphers | Set-MySecureCipher
I've left out the meaty details of a lot of this in favor of explaining at a high-level, but you can continue to research each of those areas:
Writing functions
Writing PowerShell modules
Writing Pester tests
Writing advanced functions (accepting pipeline input, supporting ShouldProcess for -WhatIf support)

Import-PfxCertificate no-ops, but no error or anything

I'm trying to import a PFX file into the local certificate store. However, Import-PfxCertificate just does nothing at all. No return value, no error, nothing:
I can double click on the PFX file in Explorer and import it with the same password, which works. Something about the PowerShell CmdLet isn't working. I've also tried other stores, such as Cert:\LocalMachine\My and TrustedPeople. Running it with -Verbose -Debug doesn't show anything extra. Nothing in the Application or Security event logs either. I'm also running as an admin. Ideas?
The Pfx file might have a cert chain. Treating it as a collection would be a better way of handling the certificate store. See installing cert chain for the C# this was based off;
[string] $certPath = '.\test.pfx';
[string] $certPass = 'MyPassword';
# Create a collection object and populate it using the PFX file
$collection = [System.Security.Cryptography.X509Certificates.X509Certificate2Collection]::new();
$collection.Import($certPath, $certPass, [System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::PersistKeySet);
try {
# Open the Store My/Personal
$store = [System.Security.Cryptography.X509Certificates.X509Store]::new('My');
$store.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite);
foreach ($cert in $collection) {
Write-Host ("Subject is: '{0}'" -f $cert.Subject )
Write-Host ("Issuer is: '{0}'" -f $cert.Issuer )
# Import the certificate into an X509Store object
# source https://support.microsoft.com/en-au/help/950090/installing-a-pfx-file-using-x509certificate-from-a-standard-net-applic
if ($cert.Thumbprint -in #($store.Certificates | % { $_.Thumbprint } )) {
Write-Warning "Certificate is already in the store"
# Force the removal of the certificate so we have no conflicts, not required if this is the first install
$store.Remove($cert)
}
# Add in the certificate
$store.Add($cert);
}
} finally {
if($store) {
# Dispose of the store once we are done
$store.Dispose()
}
}

Export certificate from object with private key Export-Clixml

I'm trying to store a few objects as Clixml from PowerShell.
I can successfully store my array and certificate, but the private key is not exported with it.
Example:
PS > $cert.HasPrivateKey
true
PS > $outObject = $array
PS > $outObject += $cert
PS > $outObject | Export-Clixml -Path .\file.xml
PS > $inObject = Import-Clixml -Path .\file.xml
PS > $newcert = $inObject | Where-Object { $_.GetType().Name -like "*X509Certificate2" }
PS > $newcert.HasPrivateKey
false
I noted that there is a method for $cert.PrivateKey:
ExportParameters Method System.Security.Cryptography.RSAParameters ExportParameters(bool includePrivateParameters)
This script is not specifically running in Windows and the certificate isn't installed in the CABI store, only the variable imported from Get-PfxCertificate.
Long story short, I'm building a module that connects to an API with client authentication. I'm trying to pull client authentication from the Clixml file.
The private key is not a part of X509Certificate2 object, thus it is not exported along with the public certificate. The private key is linked to the public certificate.
In order to export a certificate with a private key, you have to serialize the certificate and private key object before passing it to Export-CliXml.
Use the X509Certificate2.Export(X509Content​Type, Secure​String) method to export the certificate with the associated private key to PFX (PKCS#12 container). The private key material is password-protected.
Use the X509Certificate2.Import(Byte[], Secure​String, X509Key​Storage​Flags) method to import the certificate and associated private key after calling the Import-CliXml cmdlet.
This is the only option you have. Also, be aware that this approach works only when the private key is exportable. If the private key is non-exportable, the Export method will fail.
By converting the certificate object to PFX format (as suggested by Crypt32) and saving my objects in a hash table I was able to successfully export and import the array and certificate with private key.
PS > $cert.HasPrivateKey
true
PS > $pfx = $cert.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pfx,'Pa$$w0rd')
PS > $outObject = #{
>> myArray = $array
>> myCert = $pfx
>> }
PS > Export-Clixml -InputObject $outObject -Path .\file.xml
PS > $inObject = Import-Clixml -Path .\file.xml
PS > $newCert = [System.Security.Cryptography.X509Certificates.X509Certificate2]::New($inObject.myCert,'Pa$$w0rd')
PS > $newCert.HasPrivateKey
true

Outputting a file with an Azure Function

I'm trying to experiment with Azure Functions. Basically my use case is calling the function with a GUID as GET Parameter, having the function download the WIX toolkit DLL and an MSI file, updating a parameter in the MSI file, and the returning that file to the caller of the function (as download prompt for example).
I'm mostly there, just need some help getting the download prompt/send to happen, my code so far:
$urlWix = "http://domain/wix.dll"
$outputWix = "$Env:TEMP\wix.dll"
Invoke-WebRequest -Uri $urlWix -OutFile $outputWix
try{Add-Type -Path $outputWix}catch{$Null}
$urlMSI = "http://domain/file.msi"
$outputFile = "$Env:TEMP\file.msi"
Invoke-WebRequest -Uri $urlMSI -OutFile $outputFile
$oDatabase = New-Object Microsoft.Deployment.WindowsInstaller.Database($outputFile,[Microsoft.Deployment.WindowsInstaller.DatabaseOpenMode]::Direct);
$sSQLQuery = "SELECT * FROM Property WHERE Property= 'MYPROPERTY'"
[Microsoft.Deployment.WindowsInstaller.View]$oView = $oDatabase.OpenView($sSQLQuery)
$oView.Execute()
$oRecord = $oView.Fetch()
$oRecord.SetString("Value","MyCustomValue")
$oView.Modify([Microsoft.Deployment.WindowsInstaller.ViewModifyMode]::Update,$oRecord)
$oView.Close();
$oDatabase.Dispose();
$file = get-item $outputFile
write-output $file
Unfortunately due to content type issues this is not possible in powershell. You can do this via a C#, F#, or Node (isRaw) function. The problem is that you need to specify headers via the JSON response format, which would convert any non-text data into a base64 string.
If you want to sent a text file via powershell it is possible:
$response = ConvertTo-JSON #{
Body="your file data";
Headers=#{
# unfortunately it seems functions does not support 'filename=...'
'Content-Disposition'='attachment';
# you would use application/octet-stream, but because it's converted to JSON you lose binary content
'Content-Type'='text/plain';
};
}
Out-File -Encoding Ascii -FilePath $res -inputObject $response