10

I'm currently using this link to store my RSA key in windows Key Container (machine-level) and it works fine, but I'm looking for a way that works for both Linux and windows because I will definitely deploy this project on Linux.

public static void StoreRSAKey(string containerName, string xmlKey)
{
#pragma warning disable CA1416 // Validate platform compatibility
    var parameters = new CspParameters
    {
        KeyContainerName = containerName
    };
#pragma warning restore CA1416 // Validate platform compatibility
    parameters.Flags = CspProviderFlags.UseMachineKeyStore;

    using RSACryptoServiceProvider? rsa = new RSACryptoServiceProvider(parameters);
    rsa.FromXmlString(xmlKey);
}

I have found several recommendations on the web but I need a more precise solution.

I'd be glad if anyone can help me through this.

Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137
Shadi
  • 277
  • 4
  • 17
  • There are PKCS#11 and CSP combinations possible, typically the CSP is build on top of a PKCS#11 library, and that library then controls a hard- or software token. PKCS#11 is the industry standard, but Microsoft... Just giving a vector, not an answer; generally you want to use system specific key stores. You could create your own wrapper or adapter around a generic solution to sign or encrypt. – Maarten Bodewes Apr 10 '22 at 11:30
  • If you want something built-in, that’s just a PKCS#8 file (password-encrypted or plain), or paired with a certificate in a PFX. There’s no built-in cross platform opaque storage. – bartonjs Apr 11 '22 at 17:11
  • @bartonjs Thank you, would you please suggest a link or a useful resource for Linux? I can handle how to switch between OSs in my code and use the appropriate method for each. – Shadi Apr 12 '22 at 04:33

1 Answers1

11

Best way to store RSA key and use it in .Net core(looking for a cross-platform solution)

IMO, the question should be, is it possible to use the RSA key in .net core for cross-platform?

I have recently built open-source encryption and decryption library, spending hours investigating the same question you asked. The short answer it is not possible to use CspParameters with Linux, it is for Windows OS (as this answer mentions). And because the answer is not possible, there is no best way.

So to start with, let's see if we can answer the question of using the RSA key in the .net core for cross-platform.

To do that it is very simple, you need to do following:

Rsa = RSA.Create();
Rsa.KeySize = 2048;

This part does not require installing the library, it is part of netstandard2.0.

That is it, now to export and import a key that you generate, you can do the following.

When you RSA.Create() first you can export the key and store it anywhere safe for later usage.

To export private key and it should be kept safe

Rsa.ToXmlString(true);

To export public key, to encrypt with

Rsa.ToXmlString(false);

When you need to import the key from a local store, you can do the following:

Rsa.FromXmlString(asymmetricKey);

This is the cross-platform compatible solution for windows, Linux, or Mac computers.

It is also possible to import certificates from a local computer using X509Certificate2 and use its public key for encryption and private key for decryption.

It is also possible to import private key parameters to RSAParameters, which requires a helper method to translate XML tags from private key file:

<RSAKeyValue>
    <Modulus>xxxx...</Modulus>
    <Exponent>xxxx</Exponent>
    <P>xxxx...</P>
    <Q>xxxx...</Q>
    <DP>xxxx...</DP>
    <DQ>xxxx...</DQ>
    <InverseQ>xxxx...</InverseQ>
    <D>xxxx...</D>
</RSAKeyValue>

But I find it easier to use FromXmlString and it is part of RSA class when creating RSA.Create(), so no need for a helper method, that said if performance means a lot for your project, you need to make a performance test to compare the results.

So finally I provide a simple example of how to store and load keys:

public static void Main(string[] args)
{
    var rsa = RSA.Create();
    rsa.KeySize = 2048;

    // public key for decrypting
    var privateKey = rsa.ToXmlString(true);
    SaveKey(@"privateKey", privateKey);

    // public key for encrypting 
    var publicKey = rsa.ToXmlString(false);
    SaveKey(@"publicKey", publicKey);

    // initialize the private for use on another instance
    var rsaAnotherPlace = RSA.Create();
    rsaAnotherPlace.KeySize = 2048;
    rsaAnotherPlace.FromXmlString(LoadKey(@"privateKey"));
}

// store my keys
public static void SaveKey(string filename, string content)
{
    var bytes = Encoding.ASCII.GetBytes(content);
    using var fs = new FileStream(filename, FileMode.Create, FileAccess.Write);
    fs.Write(bytes, 0, bytes.Length);
}

// load key
public static string LoadKey(string filename)
{
    var bytes = File.ReadAllBytes(filename);
    return Encoding.ASCII.GetString(bytes);
}

I have tested the solution on Windows and Linux OS and it passes the macOS test on GitHub actions, but I have not tested it yet on macOS.

Disclaimer: this is the open-source library I am working on.

Maytham Fahmi
  • 31,138
  • 14
  • 118
  • 137