使用 BouncyCastle 在 C# 中读取 DER 私钥

问题描述 投票:0回答:2

我正在尝试使用 BouncyCastle 将 RSA 私钥读入 .Net 来测试我之前加密的数据。使用公钥和 Bouncy Castle 加密的数据工作正常,我还使用了与下面相同的私钥(DER 格式)在 PHP 应用程序中成功解密我的数据,但我不知道为什么我不能在 .Net 中创建私钥以执行相同的操作:

byte[] privatekey = File.ReadAllBytes(@"C:\Users\Luke\privkey.der");
var rsaKeyParameters = (RsaKeyParameters)PrivateKeyFactory.CreateKey(privatekey);

第二行抛出异常:

“工厂中的未知对象:DerInteger 参数名称:obj"

我还尝试使用流而不是字节数组,并且发生了相同的错误。密钥对是使用 OpenSSL 创建的,如上所述,在 PHP 中使用 openssl_private_decrypt() 和与 .Net 代码中相同的密钥进行解密。我还尝试了相同密钥的 PEM 格式,但这也不起作用(但我不认为 BC 直接支持 PEM)

以前有人这样做过吗?谢谢

c# encryption bouncycastle
2个回答
8
投票

问题是我假设 PublicKeyFactory 和 PrivateKeyFactory 是互补的,因为它们位于同一名称空间中。他们不是!

要解码私钥,我需要以下替代代码:

var privKeyObj = Asn1Object.FromStream(privatekey);
var privStruct = new RsaPrivateKeyStructure((Asn1Sequence)privKeyObj);

// Conversion from BouncyCastle to .Net framework types
var rsaParameters = new RSAParameters();
rsaParameters.Modulus = privStruct.Modulus.ToByteArrayUnsigned();
rsaParameters.Exponent = privStruct.PublicExponent.ToByteArrayUnsigned();
rsaParameters.D = privStruct.PrivateExponent.ToByteArrayUnsigned();
rsaParameters.P = privStruct.Prime1.ToByteArrayUnsigned();
rsaParameters.Q = privStruct.Prime2.ToByteArrayUnsigned();
rsaParameters.DP = privStruct.Exponent1.ToByteArrayUnsigned();
rsaParameters.DQ = privStruct.Exponent2.ToByteArrayUnsigned();
rsaParameters.InverseQ = privStruct.Coefficient.ToByteArrayUnsigned();
var rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
return Encoding.UTF8.GetString(rsa.Decrypt(Convert.FromBase64String(ciphertext), true));

非常感谢 owlstead 的帮助。


0
投票

我编写了一个函数,该函数从

Stream
读取并自动检测私钥是 PEM 还是 DER,以及它是 PKCS1 还是 PKCS8 编码。如下:

public static RsaPrivateCrtKeyParameters ReadPrivateKey(Stream stream)
{
    int firstByte = stream.ReadByte();
    if (firstByte < 0)
        throw new Exception($"File reached eof");

    stream.Position = 0;

    // First byte of DER is 0x30 https://stackoverflow.com/a/1362172/213871
    bool isPem = firstByte != 0x30;
    if (isPem)
    {
        using (var strReader = new StreamReader(stream))
        {
            var pemReader = new PemReader(strReader);
            while (strReader.Peek() != -1)
            {
                var obj = pemReader.ReadObject();
                var privatekey = obj as RsaPrivateCrtKeyParameters;
                if (privatekey != null)
                {
                    // It's a PKCS#8 encoded private key
                    return privatekey;
                }
                else
                {
                    // When reading a PKCS#1 encoded private key BC wants
                    // to parse a key pair instead, but the private
                    // parameter seems to be correct
                    var pair = obj as AsymmetricCipherKeyPair;
                    if (pair != null && pair.Private != null)
                    {
                        return (RsaPrivateCrtKeyParameters)pair.Private;
                    }
                }
            }
        }
    }
    else
    {
        var privKeyObj = Asn1Object.FromStream(stream);
        var sequence = (Asn1Sequence)privKeyObj;
        if (sequence[2] is Asn1OctetString)
        {
            // It's a PKCS#8 encoded private key
            AsymmetricKeyParameter keyPair = PrivateKeyFactory.CreateKey(PrivateKeyInfo.GetInstance(privKeyObj));
            return (RsaPrivateCrtKeyParameters)keyPair;
        }
        else
        {
            // Assume it's a PKCS#1 encoded private key
            var privStruct = RsaPrivateKeyStructure.GetInstance(privKeyObj);
            return new RsaPrivateCrtKeyParameters(privStruct);
        }
    }

    throw new Exception("Could not read private key");
}
© www.soinside.com 2019 - 2024. All rights reserved.