使用Bouncy Castle仅读取PEM RSA公钥

18
我正在尝试使用C#读取一个只包含RSA公钥的.pem文件。我无法访问私钥信息,也不需要它。该文件名为myprivatekey.pem,以-----BEGIN PUBLIC KEY-----开头,以-----END PUBLIC KEY-----结尾。
我的当前代码如下:
    Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair keyPair;

    using (var reader = File.OpenText(@"c:\keys\myprivatekey.pem"))
        keyPair = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();
然而,该代码会抛出一个名为“InvalidCastException”的异常,并带有如下消息:
无法将类型为“Org.BouncyCastle.Crypto.Parameters.DsaPublicKeyParameters”的对象强制转换为类型“Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair”。
当没有私钥信息可用时,我该如何使用Bouncy Castle的PemReader仅读取公钥?

2
一个单独的公钥并不是密钥对,密钥对是由公钥和私钥组成的。 - President James K. Polk
6个回答

25
以下代码将从给定的文件名中读取公钥。对于任何生产代码,异常处理都应该进行更改。该方法返回一个AsymmetricKeyParameter
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;
}

这个能用来读取私钥吗?只需要在cipher函数中传入false,如:cipher.Init(false, privatekey)。我尝试了但没有成功。 - C0D3

14

以下是一种可能的解决方案,可以将公钥和私钥PEM文件都读入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());
    }
}

希望这能帮助到某些人。


对于 Linux / .NET 标准,我最终只是使用无参数构造函数创建了一个新的 RSACryptoServiceProvider。 - jjxtra

3

改为:

keyPair = (Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();

使用:

keyPair = (Org.BouncyCastle.Crypto.AsymmetricKeyParameter)new Org.BouncyCastle.OpenSsl.PemReader(reader).ReadObject();

由于您仅使用公钥,实际上并没有一对密钥(公钥和私钥),因此您不能将其转换为“AsymmetricCipherKeyPair”,而应该转换为“AsymmetricKeyParameter”。


2
作为对c0d3Junk13的回应,我在处理PEM私钥时遇到了同样的问题,花费了整个下午使用C# BouncyCastle 1.7版本和Visual Studio 2013 Desktop Express找到了解决方案。别忘了添加对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));

2
编辑: 看起来这取决于您使用的密钥文件类型。对于ssh-keygen密钥,私钥似乎具有AsymmetricCipherKeyPair类型,但是对于openssl密钥,私钥具有RsaPrivateCrtKeyParameters类型。
Bryan Jyh Herng Chong的答案对我不再起作用(至少在Bouncy Castle版本v1.8.5中)。似乎kp.GetType().GetProperty("Private")不再针对公共和私有密钥PEM对象设置不同的值。现在使用PemReader.ReadObject()返回的对象直接是RsaPrivateCrtKeyParameters对象,因此不再需要先通过AsymmetricCipherKeyPair对象进行转换。
我将那行代码更改为以下内容,它完美地工作了:
return (kp.GetType() == typeof(RsaPrivateCrtKeyParameters)) ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)kp)) : MakePublicRCSP(rsaKey, (RsaKeyParameters)kp);

-1
尝试以下代码:
Using Org.BouncyCastle.Crypto;


string path = HttpContext.Current.Server.MapPath(@"~\key\ABCD.pem");



AsymmetricCipherKeyPair Key;

TextReader tr = new StreamReader(@path);

 PemReader pr = new PemReader(tr);
        Key = (AsymmetricCipherKeyPair)pr.ReadObject();
        pr.Reader.Close();
        tr.Close();



         AsymmetricKeyParameter keaa = Key.Public;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接