34

I'm trying to encrypt and decrypt some data with RSA. I've looked up teh RSA class but I'm only seeing the abstract class https://msdn.microsoft.com/en-us/library/system.security.cryptography.rsa(v=vs.110).aspx

I've read that the RSA class in DNX5 is different from the one in .net 4.6.1 Is that a different one than what I'm seeing? If so where can I find the documentation to use that one? It also appears that RSACryptoServiceProvider is not working in .net core and I only have access to the RSA abstract class.

mysticalstick
  • 2,479
  • 5
  • 16
  • 21

2 Answers2

90

You should avoid using RSACryptoServiceProvider if you can. It only works on Windows (and it's the less good RSA implementation on Windows). Stick to the RSA base class, and create new instances via RSA.Create()

Ephemeral Keys (Creation)

.NET Core

using (RSA rsa = RSA.Create())
{
    rsa.KeySize = desiredKeySizeInBits;

    // when the key next gets used it will be created at that keysize.
    DoStuffWithThePrivateKey(rsa);
}

.NET Framework <= 4.7.1

Unfortunately, the default for RSA.Create() on .NET Framework is RSACryptoServiceProvider, which doesn't respect set_KeySize. So if you need ephemeral keys you'll need to use different code on .NET Framework vs .NET Core:

using (RSA rsa = new RSACng())
{
    rsa.KeySize = desiredKeySizeInBits;

    DoStuffWithThePrivateKey(rsa);
}

Or, if you need to support versions earlier than 4.6 (where RSACng didn't exist) / 4.6.2 (where most of the .NET Framework worked happily with RSACng objects instead of RSACryptoServiceProvider objects) you can continue to use the older implementation:

using (RSA rsa = new RSACryptoServiceProvider(desiredKeySizeInBits))
{
    // Since before net46 the Encrypt/Decrypt, SignData/VerifyData, SignHash/VerifyHash
    // methods were not defined at the RSA base class, you might need to keep this strongly
    // typed as RSACryptoServiceProvider.
    DoStuffWithThePrivateKey(rsa);
}

.NET Framework >= 4.7.2

Here we have an overload Create(Int32) that allows specifying a key size and - on Windows - returns an instance of the modern RSACng. But caution: The parameterless overload Create() still returns the outdated RSACryptoServiceProvider.

Ephemeral Keys (Import)

Even though RSACng, in general, is easier to work with than RSACryptoServiceProvider, RSACryptoServiceProvider should work fine in this context, so RSA.Create() is good on all platforms:

using (RSA rsa = RSA.Create())
{
    rsa.ImportParameters(rsaParameters);

    DoStuffWithWhateverKindOfKeyYouImported(rsa);
}

From a certificate:

.NET Core 1.0+, .NET Framework 4.6+

using (RSA rsa = cert.GetRSAPublicKey())
{
    DoStuffWithThePublicKey(rsa);
}

or

using (RSA rsa = cert.GetRSAPrivateKey())
{
    DoStuffWithThePrivateKey(rsa);
}

.NET Framework < 4.6

// Do NOT put this in a using block, since the object is shared by all callers:
RSA rsaPrivate = (RSA)cert.PrivateKey;
DoStuffWithThePrivateKey(rsaPrivate);

// Do NOT put this in a using block, since the object is shared by all callers:
RSA rsaPublicOnly = (RSA)cert.PublicKey.Key;
DoStuffWithThePublicKey(rsaPublic);

Using Named/Persisted Keys (Windows-only)

I was going to include some samples about RSACryptoServiceProvider (WinXP+/CAPI) and RSACng (Win7+/CNG) creating/opening named keys, but that's not a very common scenario in .NET; and it certainly isn't portable (portability to other OSes being one of the more compelling arguments for .NET Core).

Referencing things.

For .NET Core 1.0 and 1.1, you get access to the RSA base class from the System.Security.Cryptography.Algorithms package. In .NET Core 2.0 it will be included in the netstandard package reference.

If you need to do complex interop with the OS you can reference System.Security.Cryptography.Cng (Windows CNG), System.Security.Cryptography.Csp (Windows CAPI/CryptoServiceProvider), or System.Security.Cryptography.OpenSsl (Linux OpenSSL, macOS OpenSSL) and get access to the interop-enabled classes (RSACng, RSACryptoServiceProvider, RSAOpenSsl). But, really, you shouldn't do that.

What does RSA.Create() return?

  • .NET Framework <= 4.7.1: RSACryptoServiceProvider, unless changed by CryptoConfig.
  • .NET Framework >= 4.7.2: ditto. But the there is a new Create(Int32) overload, which takes a key size and returns a RSACng on Windows.
  • .NET Core (Windows): A private class which implements RSA via CNG, you can't cast it to any more specific type.
  • .NET Core (Linux): A private class which implements RSA via OpenSSL, you can't cast it to any more specific type.
  • .NET Core (macOS): A private class which implements RSA via OpenSSL, you can't cast it to any more specific type. (This should be via SecureTransforms in the next release of .NET Core)
Carsten Führmann
  • 3,119
  • 4
  • 26
  • 24
bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Hi Barton, can you post a working example code of using OpenSSL in linux. I deploy my project in iss with RSACryptoServiceProvider and CspParameters for generation of a jwt and it work well. [Git Repo](https://github.com/lonemario/SHS.Authentication) – Mario Righi Nov 29 '17 at 16:08
  • @MarioRighi I’m not sure what you’re asking for. Possibly you want to just ask a different / more specific new question. – bartonjs Nov 29 '17 at 17:10
  • I am targeting .NET Standard 2.0, but `rsa.KeySize = desiredKeySizeInBits` does not have any effect? – Rosdi Kasim Apr 07 '18 at 03:52
  • @RosdiKasim The actual runtime is what matters, not the target. `RSA rsa = RSA.Create(); if (rsa is RSACryptoServiceProvider rsaCsp) { rsaCsp.Dispose(); rsa = new RSACng(); } rsa.KeySize = desiredKeySizeInBits;` is one possible workaround. – bartonjs Apr 07 '18 at 03:59
  • Ah.. finally all my tests are green again... what a beauty. Thanks! – Rosdi Kasim Apr 07 '18 at 04:39
  • I am trying to follow your recommendation var rsa = RSA.Create(); rsa.ImportParameters(rp); which looks the best for me; but I am having an issue while using System.Security.Cryptography.Pkcs.CmsSigner.ComputeSignature: System.ArgumentException : The CNG key handle being opened was detected to be ephemeral, but the EphemeralKey open option was not specified. How can I fix that ? Thanks a lot – forlayo Jan 28 '19 at 10:48
  • @forlayo You should ask that as a different question, so that you can show your code and the exception stack trace. – bartonjs Jan 28 '19 at 15:56
  • Thanks @bartonjs, I've posted a new question as you suggest here: https://stackoverflow.com/questions/54479122/rsa-and-signedcms-on-asp-net-core-multiplatform-approach-needed – forlayo Feb 01 '19 at 12:03
6

You will need to use the dotnetcore library instead.
Add the System.Security.Cryptography.Algorithms Nuget package to your project.

It provides all the common Algorithms for your project.

Here is the RSACryptoServiceProvider class that you would use for the Encrypt() and Decrypt() methods.

Hakan Fıstık
  • 16,800
  • 14
  • 110
  • 131
truemedia
  • 1,042
  • 6
  • 12
  • I'm new to .net core and I've added the package through Nuget. Is there anywhere where I can find the documentation for this and do I not use the `RSA` class? (I will consider more secure algorithms in the future this task is required to be RSA). Thanks. – mysticalstick Feb 01 '17 at 19:33
  • There really isn't much documentation, unfortunately. But I did manage to find the [RSACryptoServiceProvider class reference page](https://learn.microsoft.com/en-us/dotnet/core/api/system.security.cryptography.rsacryptoserviceprovider) which includes all the class information. Hopefully that will help you implement. – truemedia Feb 01 '17 at 19:42
  • Since when is RSA not a secure algorithm, or more directly, what asymmetric algorithms would you recommend over RSA? Additionally, the OP never described his threat model, so it could be perfect for what he is doing. – Luke Joshua Park Feb 01 '17 at 21:34
  • Sure it's not relevent to the answer, removed the opinion. – truemedia Feb 01 '17 at 22:00
  • 1
    I'm curious as to why you said a "more secure" algorithm than RSA would be better? – Luke Joshua Park Feb 01 '17 at 22:05