Benjineer
Benjineer

Reputation: 1680

Unable to create a CngKey from a PEM file in C#

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

Answers (2)

XzajoX
XzajoX

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

Access Denied
Access Denied

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

Related Questions