Reputation: 1680
I'm trying to create a CngKey
in .NET 4.7.2 from a PEM file so I can sign an Apple auth JWT token.
It all worked fine in dev, but when I deployed to staging I was met with a "file not found error" from calling CngKey.Import()
. Of course. The app is running under some IIS app pool user with no user directory structure for the OS to store keys in.
Since then I've been trying to use CngKey.Create()
to import a machine-wide key, but I keep getting the following unhelpful error:
System.Security.Cryptography.CryptographicException: 'The requested operation is not supported.'
This exception was originally thrown at this call stack:
System.Security.Cryptography.NCryptNative.SetProperty(Microsoft.Win32.SafeHandles.SafeNCryptHandle, string, byte[], System.Security.Cryptography.CngPropertyOptions)
System.Security.Cryptography.CngKey.SetKeyProperties(Microsoft.Win32.SafeHandles.SafeNCryptKeyHandle, System.Security.Cryptography.CngKeyCreationParameters)
System.Security.Cryptography.CngKey.Create(System.Security.Cryptography.CngAlgorithm, string, System.Security.Cryptography.CngKeyCreationParameters)
SigningTest.Program.MachineKey() in Program.cs
SigningTest.Program.Main() in Program.cs
Here is my current code:
var keyParameters = new CngKeyCreationParameters
{
ExportPolicy = CngExportPolicies.AllowPlaintextExport,
KeyCreationOptions = CngKeyCreationOptions.MachineKey,
KeyUsage = CngKeyUsages.AllUsages,
Provider = CngProvider.MicrosoftSoftwareKeyStorageProvider,
UIPolicy = new CngUIPolicy(CngUIProtectionLevels.None)
};
// KeyMeat is the part of the PEM file between "-----BEGIN PRIVATE KEY-----" and "-----END PRIVATE KEY-----"
var keyBytes = Convert.FromBase64String(KeyMeat);
keyParameters.Parameters.Add(new CngProperty(
CngKeyBlobFormat.GenericPrivateBlob.Format,
keyBytes,
CngPropertyOptions.None)
);
var privateKey = CngKey.Create(CngAlgorithm.ECDsaP256, "someName", keyParameters);
Note that the error occurs at CngKey.Create()
, but only when keyParameters.Parameters.Add()
is not commented out.
Upvotes: 0
Views: 1350
Reputation: 23
This may help someone with the same problem as I had. My problem with the above solution is, that we are not allowed to change IIS settings on the deployment machine, because of their policy.
I browsed around and kept hitting solutions with BouncyCastle. I tried to avoid it (my personal policy), but finally decided to use it. This is the post, that helped me
Here's my code:
byte[] data = Encoding.ASCII.GetBytes(textToEncrypt); // data to be signed
// GetRSAFromPem() is a method from the post mentioned
// PEMkey is a string in format "-----BEGIN PRIVATE KEY----- ***the key*** -----END PRIVATE KEY-----"
// this works and doesn't ask for profile access
using (RSA rsa = GetRSAFromPem(PEMkey))
{
return Convert.ToBase64String(rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1));
}
Upvotes: 0
Reputation: 9461
Open IIS and specify LoadUserProfile for your pool: Alternatively you can open AppSettings in your IIS and set
WEBSITE_LOAD_USER_PROFILE = 1
Upvotes: 1