4

I am trying to load the Private and Public keys from a PEM file using .Net Core. My code looks like this:

var localPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
var path = Path.Combine(localPath, this._configManager.JwtPem);
var rsaCryptoServiceProvider = new RSACryptoServiceProvider();

var linesList = File.ReadAllLines(path).ToList();
var line = string.Concat(linesList.GetRange(1, linesList.Count - 2));

rsaCryptoServiceProvider.ImportCspBlob(Convert.FromBase64String(line));

The exception I am getting is:

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException : Bad Version of provider
   at Internal.NativeCrypto.CapiHelper.ImportKeyBlob(SafeProvHandle saveProvHandle, CspProviderFlags flags, Boolean addNoSaltFlag, Byte[] keyBlob, SafeKeyHandle& safeKeyHandle)
   at System.Security.Cryptography.RSACryptoServiceProvider.ImportCspBlob(Byte[] keyBlob)
   at StepNexusCA.ServiceLayer.Authorization.TokenService.GenerateToken(List`1 claims)

The PEM file containing the PKCS1 format of my development Private/Public keys is here:

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAwgs8kmIwk+4geRO7dGZjzYpgD2OiaUrnOOIk+ObXt/CcjhwX
lSst+jBmfMF1Wp/mF4aUQsePxN59MYV2BsqPLEkzVdq/fb/7V2wbZcooJAQKkJwT
emtYHrBN00KBBeu9uQZlFOw365ij4GrbP7mcr4tNFZ3TPnRFUUFqhvB6mEG1aZsb
lOn1lgL34tAycQHNxttXz/aGfPyTefQ+yISvSY2n8288OVlyfu6wKDONQYS+/stC
tCV+a+/dDUSUjaZsXM1+BMSflsINqIcCTCMvPa6fb5Z+USfPDcDNwzUyX20LBzH5
wFwPLIvuoqJeeczcaHaT+dS2ZZREj6kgUsdC+QIBAwKCAQEAgVzTDEF1t/QVpg0n
ou7tM7GVX5fBm4dE0JbDUJnlJUsTCWgPuMdz/CBEUyujkb/uulm4LIUKgz7+IQOk
BIcKHYYiOTx/qSqnj51nmTFwGAKxtb1iUZzlacrejNcArp0pJgRDY0glR7sX6vHn
f9ETH7IzY76M1E2Di4Dxr0r8ZX/3ozsrSXp+GMJLeN9sCjKSyxoE5Y71eDBTCX2N
tShJJjhqUDz61bhKlX9j5c3jWvTXx46dE8wjoJ/BW1XJo5J1gzHQ/OLYeOXIdxlj
jVSlEuU69UT588B7UEEK9N9xK5K/c0Yw5gd02RUv/o7qdpYQICeGtQMMaFkm75xy
nUOxwwKBgQD/orUvgNJfFKyvGY8XJTuek5q8IcFD8AFO3b7pNnPynw8llyEpACAv
Onf9aJSPZvtrabSqrpO8k8Ijyhe2Ino39GuRV8RURl46GmFN31RoYV1wHI4K7Emh
68cdKbCEBudog+kImImldBAfo+QmBtqhS+u4B5qQwwnFa8DriQoiYwKBgQDCUg0r
Jd/ZXDLXk/H5PHpTApmUVd7SWLLIDfkBAlRO8Sni4/Ka+KTTZDec5uoo0hoP6cCs
Z9+MZz4XOiwv9dCEI5czMawGmwsm23+fGM/PP/lW4yD8dz10KZggKjWElymDVl+n
zsc6ctwHAOfYwREi7E+R4rWTBgTEvH2I3deV8wKBgQCqbHjKVeGUuHMfZl9kw30U
YmcoFoDX9VY0k9SbeaKhv19uZMDGABV00aVTmw2071JHm83HHw0oYoFtMWUkFvwl
TZ0Lj9g4Lul8EZYz6jhFlj5KvbQHSDEWnS9oxnWtWe+bAptbEFvDorVqbULEBJHA
3UfQBRG111vY8oCdBgbBlwKBgQCBjAjHbpU7ksyPt/amKFGMrGZi4+nhkHcwCVCr
VuLfS3FB7UxnUG3iQs+970bF4Wa1RoBy7+pdmilk0XLKo+BYF7oiIR1ZvLIZ56pq
EIqKKqY57MCoT35NcRAVcXkDD3ECOZUaidom9z1aAJqQgLYXSDUL7HkMrq3YfakF
6Tpj9wKBgEPCSW7EMFjK2NzmB+4b+skxXcfCZ0ldNtwoUDijuAMFg8ueC3j2qFUX
bAXSApi3mQMow1/JwQxiZ+b+GDLdTcE/PrBVBRkL/5RkmnVagbjBrdZhVjpC+dUo
eEkCChClGGpRyPJ+DYYRyX1Fk9Und8Xbd49Vv+/6RL76ys3gGQl8
-----END RSA PRIVATE KEY-----

Why can't I import the key using ImportCspBlob(...)? I have not found much info online regarding the exception but where is my code wrong? I am aware of BouncyCastle but I am trying to do this natively using .Net Core.

VasilisP
  • 136
  • 2
  • 11
  • Very simply, you can't import it because it is not a supported format. – President James K. Polk Nov 22 '18 at 16:34
  • But then what is the format supported? Not so clear here. https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.icspasymmetricalgorithm.importcspblob?view=netcore-2.1#System_Security_Cryptography_ICspAsymmetricAlgorithm_ImportCspBlob_System_Byte___ – VasilisP Nov 22 '18 at 16:40
  • Not clear at all, is it? I believe the format is a Microsoft proprietary format that you'd get by calling ExportCspBlob. I'm not sure how to get from what you have, which is a PKCS#1 RSAPrivateKey object, into RSACryptoServiceProvider. – President James K. Polk Nov 22 '18 at 16:48
  • If you are referring to the XML format and I have it all working using it. However the PKCS#1 format is industry accepted. I do not understand why MS is not supporting it... – VasilisP Nov 22 '18 at 16:57
  • 1
    I think I read somewhere that one of these upcoming versions of .NET will support some of these more widely used formats, but I don't recall the details. – President James K. Polk Nov 22 '18 at 17:02

4 Answers4

9

The format for ImportCspBlob is the format from ExportCspBlob, which is the PRIVATEKEY blob format required by CryptImportKey. Since .NET just transparently passes that on to Windows CAPI, the ImportCspBlob method throws on non-Windows platforms.

Another answer that I've given in the past for importing private keys (including PKCS#1 RSAPrivateKey) is a bit of a meta-answer, which includes links to just get things working: Digital signature in c# without using BouncyCastle.

.NET Core 3.0's daily builds have the functionality built-in. Mostly. The PEM format is easy in practice, but somewhat annoying in the spec, so the methods leave it up to the caller to "un-PEM" the data... for the default formatting on a single-value payload with no attributes (like you have in your example) you can do it with daily builds via

private static RSA ReadKeyFromFile(string filename)
{
    string pemContents = System.IO.File.ReadAllText(filename);
    const string RsaPrivateKeyHeader = "-----BEGIN RSA PRIVATE KEY-----";
    const string RsaPrivateKeyFooter = "-----END RSA PRIVATE KEY-----";

    if (pemContents.StartsWith(RsaPrivateKeyHeader))
    {
        int endIdx = pemContents.IndexOf(
            RsaPrivateKeyFooter,
            RsaPrivateKeyHeader.Length,
            StringComparison.Ordinal);

        string base64 = pemContents.Substring(
            RsaPrivateKeyHeader.Length,
            endIdx - RsaPrivateKeyHeader.Length);

        byte[] der = Convert.FromBase64String(base64);
        RSA rsa = RSA.Create();
        rsa.ImportRSAPrivateKey(der, out _);
        return rsa;
    }

    // "BEGIN PRIVATE KEY" (ImportPkcs8PrivateKey),
    // "BEGIN ENCRYPTED PRIVATE KEY" (ImportEncryptedPkcs8PrivateKey),
    // "BEGIN PUBLIC KEY" (ImportSubjectPublicKeyInfo),
    // "BEGIN RSA PUBLIC KEY" (ImportRSAPublicKey)
    // could any/all be handled here.
    throw new InvalidOperationException();
}

Daily builds of the .NET Core SDK can be obtained from https://github.com/dotnet/core-sdk/#installers-and-binaries

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • Thanks for the answer. Are you suggesting this is not possible with .Net Core 2.1? Is that why I cannot resolve ImportRSAPrivateKey(...) or is that your own extension method? – VasilisP Nov 23 '18 at 15:57
  • The method is new in .NET Core 3.0; .NET Core 2.1 doesn’t have built in public API for it – bartonjs Nov 23 '18 at 17:16
  • Thank you. Obviously, that means I have to rely on a beta release to go to production. I expect once v3 is out, I will revisit this area. Thank you for your help. – VasilisP Nov 29 '18 at 16:56
1

There are a lot of Convert PEM to XML online tools, just convert your pem to xml, then

RSA.FromXmlString(string xmlString)

huang
  • 919
  • 11
  • 22
0

If you don't need to convert from PEM to DER in the code, you can use openssl to get the DER encoded private key file:

openssl rsa -in key.pem -out key.der -outform der

Lucas Martins
  • 553
  • 3
  • 8
0

The .Net Cryptographic API does not support the industry widely used PEM files so we need to convert it to the XML format, introduced by Microsoft. Basically, the solution was found in another similar question here C# Extract public key from RSA PEM private key.

VasilisP
  • 136
  • 2
  • 11