3

I recently had to move an internal tooling library from .NET Core 3.0 to 2.1. In doing so I encountered that RSA.ImportPkcs8PrivateKey() vanished; unfortunately, I needed that method.

I went looking (admittedly briefly) for alternative methods that didn't involve third-party libraries, but didn't find much (best Google was this unanswered SO post).

I decided to stick my hand in the fire and risk a burn for an interim fix. I wrote the below (disclaimer: abridged/untested form of working solution) to reflect into some of the 2.1 internal types/methods and achieve the same results of RSA.ImportPkcs8PrivateKey().

I'm curious if anyone has a better (and less risky) solve? The constraints I'm working under are

  • framework .NET Core 2.1
  • no external libraries
  • no custom PKCS parsing code
  • resultant type is System.Security.Cryptography.RSA
  • Input is a PKCS#8 private key in PEM format. The below code requires pre-call conversion from PEM to DER.
    -----BEGIN PRIVATE KEY-----
    MIIEvAIBAD...wHi59v+A==
    -----END PRIVATE KEY-----

I'm guessing I've missed some lesser known method or perhaps an indirect route via an intermediate transformation (I've seen refs to going via PKCS#11)? If not, then here's a small contribution to lightening my massive SO debt.

Thanks all for saving my bacon over the years!



public static void ImportPkcs8PrivateKey(this RSA rsa, ReadOnlySpan<byte> source, out int bytesRead)
{
    var assembly = typeof(RSA).Assembly;
    var derSequenceReader = assembly.GetType("System.Security.Cryptography.DerSequenceReader", true);

    var readerArgs = new[] { source.ToArray() };
    var reader = Activator.CreateInstance(
        derSequenceReader,
        BindingFlags.Instance | BindingFlags.NonPublic,
        null, readerArgs, null);

    var helperType = assembly.GetType("System.Security.Cryptography.RsaKeyBlobHelpers", true);
    var readPkcs8Blob = helperType.GetMethod(
        "ReadPkcs8Blob",
        BindingFlags.Static | BindingFlags.NonPublic);

    var methodArgs = new[] { reader, new RSAParameters() };
    readPkcs8Blob.Invoke(reader, methodArgs);

    // ReadPkcs8Blob() takes an RSAParameterr object as a 'ref'.
    var parameters = (RSAParameters)methodArgs[1];
    rsa.ImportParameters(parameters);
}
Russell Speight
  • 344
  • 3
  • 8
  • You could do some caching and use dynamic methods if this gets called in the hot path. Reflection is expensive. Other than that looks good to me. – Zer0 Feb 04 '21 at 22:12
  • Yerp, already done ;-). The above is a simplified form for readability. – Russell Speight Feb 04 '21 at 22:16

0 Answers0