Reading PEM RSA Public Key Only using Bouncy Castle

Here's a possible solution that reads both public and private PEM files into RSACryptoServiceProvider:

public class PemReaderB
{
    public static RSACryptoServiceProvider GetRSAProviderFromPem(String pemstr)
    {
        CspParameters cspParameters = new CspParameters();
        cspParameters.KeyContainerName = "MyKeyContainer";
        RSACryptoServiceProvider rsaKey = new RSACryptoServiceProvider(cspParameters);

        Func<RSACryptoServiceProvider, RsaKeyParameters, RSACryptoServiceProvider> MakePublicRCSP = (RSACryptoServiceProvider rcsp, RsaKeyParameters rkp) =>
        {
            RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
            rcsp.ImportParameters(rsaParameters);
            return rsaKey;
        };

        Func<RSACryptoServiceProvider, RsaPrivateCrtKeyParameters, RSACryptoServiceProvider> MakePrivateRCSP = (RSACryptoServiceProvider rcsp, RsaPrivateCrtKeyParameters rkp) =>
        {
            RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
            rcsp.ImportParameters(rsaParameters);
            return rsaKey;
        };

        PemReader reader = new PemReader(new StringReader(pemstr));
        object kp = reader.ReadObject();

        // If object has Private/Public property, we have a Private PEM
        return (kp.GetType().GetProperty("Private") != null) ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)kp).Private)) : MakePublicRCSP(rsaKey, (RsaKeyParameters)kp);
    }

    public static RSACryptoServiceProvider GetRSAProviderFromPemFile(String pemfile)
    {
        return GetRSAProviderFromPem(File.ReadAllText(pemfile).Trim());
    }
}

Hope this helps someone.


In answer to c0d3Junk13, I had the same issue for a PEM private key and it took me all afternoon to find the solution using the C# BouncyCastle Version 1.7 and Visual Studio 2013 Desktop Express. Don't forget to add the project reference to BouncyCastle.Crypto.dll

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Crypto.Digests;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Signers;
using Org.BouncyCastle.X509;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Math.EC;
using Org.BouncyCastle.Utilities.Collections;
using Org.BouncyCastle.Utilities.Encoders;
using Org.BouncyCastle.Crypto; 
using Org.BouncyCastle.Crypto.Engines; 
using Org.BouncyCastle.OpenSsl;

/* 
    For an Active Directory generated pem, strip out everything in pem file before line:
    "-----BEGIN PRIVATE KEY-----" and re-save.
*/
string privateKeyFileName = @"C:\CertificateTest\CS\bccrypto-net-1.7-bin\private_key3.pem";

TextReader reader = File.OpenText(privateKeyFileName);

Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters key;

using (reader = File.OpenText(privateKeyFileName))
{
    key = (Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters)new PemReader(reader).ReadObject();
}

cipher.Init(false, key);

//Decrypting the input bytes

byte[] decipheredBytes = cipher.ProcessBlock(cipheredBytes, 0, cipheredBytes.Length);

MessageBox.Show(Encoding.UTF8.GetString(decipheredBytes));

EDIT: It looks like this depends on what type of key file you are using. For ssh-keygen keys, the private key appears to have a type of AsymmetricCipherKeyPair, but for openssl keys, the private key has a type of RsaPrivateCrtKeyParameters.


Bryan Jyh Herng Chong's answer no longer appears to work for me (at least with Bouncy Castle version v1.8.5). It appears kp.GetType().GetProperty("Private") is no longer set differently for public vs private key PEM objects. It also appears that the object returned using PemReader.ReadObject() is now directly a RsaPrivateCrtKeyParameters object, so there's no longer a need to cast through a AsymmetricCipherKeyPair object first.

I changed that line to this and it worked like a charm:

return (kp.GetType() == typeof(RsaPrivateCrtKeyParameters)) ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)kp)) : MakePublicRCSP(rsaKey, (RsaKeyParameters)kp);

The following code will read a public key from a given filename. The exception handling should be changed for any production code. This method returns an AsymetricKeyParameter:

public Org.BouncyCastle.Crypto.AsymmetricKeyParameter ReadAsymmetricKeyParameter(string pemFilename)
{
    var fileStream = System.IO.File.OpenText(pemFilename);
    var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(fileStream);
    var KeyParameter = (Org.BouncyCastle.Crypto.AsymmetricKeyParameter)pemReader.ReadObject();
    return KeyParameter;
}