1

I'm trying to use SignData method of .NET RSACryptoServiceProvider class from Powershell.

On Windows 10/Powershell 5.1/.NET 4.7, the following code works reliably:

$toSign = [System.Text.Encoding]::UTF8.GetBytes("ABCDE")

$cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2
$cert.import("c:\ps\GAdmin\apiaccess.pfx","notasecret","Exportable,PersistKeySet")
$cert | fl *

$params = New-Object System.Security.Cryptography.CspParameters
$params.KeyContainerName = $cert.PrivateKey.CspKeyContainerInfo.KeyContainerName
$params.ProviderName = "Microsoft Enhanced RSA and AES Cryptographic Provider"
$params
$cert.PrivateKey.CspKeyContainerInfo.KeyNumber
$params.KeyNumber = 1
$rsa = New-Object System.Security.Cryptography.RSACryptoServiceProvider($params)
$tosign
$rsa.SignData($toSign,"SHA256")

However, on Linux (or Windows) with .NET Core/Powershell Core, I'm having problems. I can instantiate an RSACryptoServiceProvider, but SignData errors out:

$cert = Get-PfxCertificate ./apiaccess.pfx -Password (ConvertTo-SecureString "notasecret" -AsPlainText -Force)
$rsa = $cert.PrivateKey
$rsa.SignData($toSign,"SHA256")

Cannot find an overload for "SignData" and the argument count: "2". At line:1 char:1 + $rsa.SignData($toSign,"SHA256") + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodException + FullyQualifiedErrorId : MethodCountCouldNotFindBest

Checking the method definition - it seems to require padding parameter:

($rsa | get-member SignData).Definition

byte[] SignData(byte[] data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding), byte[] SignData(byte[] data, int offset, int count, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding), byte[] SignData(System.IO.Stream data, System.Security.Cryptography.HashAlgorithmName hashAlgorithm, System.Security.Cryptography.RSASignaturePadding padding)

So my question is: how do I specify padding and call SignData from Powershell Core?

  • I had thought there were issues with the RSACryptoServiceProvider provider on Linux (https://github.com/dotnet/corefx/issues/17410). To your question about Windows Core, check out this post https://stackoverflow.com/questions/41986995/implement-rsa-in-net-core/42006084. Both answers are from Jeremy Barton at Microsoft. The question on stack should give you what you need. – Adam May 04 '18 at 13:34
  • @Adam - you're right, the issue is not specific to this provider. The behavior has changed, and affects RSACng and OpenSSL providers as well; I should have been more precise. – Svyatoslav Pidgorny May 06 '18 at 02:56

1 Answers1

2

It's possible that $cert.PrivateKey returned something other than RSACryptoServiceProvider. You really should avoid calling that property, and should replace it with $cert.GetRSAPrivateKey() (or whatever algorithm you expect the key to be... it returns null if you get it wrong so exceptions aren't a concern). Not that it'll help, except that it's guaranteed to never return RSACryptoServiceProvider on Linux (and "almost guaranteed" to not return it on Windows). Though it would solve your need to re-interpret the CSP parameters on Windows.

Instead of calling `

$rsa.SignData($toSign, "SHA256")

you should call

$rsa.SignData(
    $toSign,
    System.Security.Cryptography.HashAlgorithmNames.SHA256,
    System.Security.Cryptography.RSASignaturePadding.Pkcs1)

I don't actually know the powershell-ese for the second two parameters, hopefully my understanding of that language's syntax is good enough to unblock you. Both the "SHA256" and "Pkcs1" here are properties, so you could save them to locals if it makes your code prettier.

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • The Powershell-ese for this is "$rsa.SignData($toSign, [Security.Cryptography.HashAlgorithmName]::SHA256, [Security.Cryptography.RSASignature Padding]::Pkcs1)" - and it works! Thank you, and thanks to Vadims Podans (@Crypt32). – Svyatoslav Pidgorny May 06 '18 at 02:49