无法在 C# 中解密由 openssl 完成的加密字符串。总是得到 Org.BouncyCastle.Security.InvalidKeyException

Cannot decrypt in C# an encrypted string done by openssl. Always get Org.BouncyCastle.Security.InvalidKeyException

我试图在 C# 中解密一个由 openssl 通过命令行加密的字符串,但它一直抛出以下异常:

Exception thrown: 'Org.BouncyCastle.Security.InvalidKeyException' in BouncyCastle.Crypto.dll
An unhandled exception of type 'Org.BouncyCastle.Security.InvalidKeyException' occurred in 
BouncyCastle.Crypto.dll
Not an RSA key

我正在使用 RSA 加密,这些是我用来生成 .pem 文件和 encrypt/decrypt 测试字符串的命令(来自 git bash):

// Create private key
// openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -pkeyopt rsa_keygen_pubexp:65537 -out privkey.pem

// Create public key
// openssl pkey -pubout -in privkey.pem -out pubkey.pem

// "helloworld" as test text
// echo helloworld > clear_text.txt

// Encrypt using public key
// openssl rsautl -encrypt -inkey pubkey.pem -pubin -in clear_text.txt -out crypt.bin

// Decrypt using private key
// openssl rsautl -decrypt -inkey privkey.pem -in crypt.bin -out decrypt.txt

在 C# 中需要做的只是上面序列的最后一行,但在 C# 中。这是我用来加载 .pem 文件(由 openssl 生成)和解密字符串的 C# 代码。

    static private PemReader publicKeyPemFile;
    static private PemReader privateKeyPemFile;
    static private string filesRootPath = System.IO.Directory.GetCurrentDirectory() + "/../../../";

    static public void LoadKeys()
    {
        publicKeyPemFile = new PemReader(
            (StreamReader)File.OpenText(filesRootPath + "pubkey.pem")
        );

        privateKeyPemFile = new PemReader(
            (StreamReader)File.OpenText(filesRootPath + "privkey.pem")
        );
    }


    static public string RSADecrypt(byte[] cipherTextBytes)
    {
        RsaKeyParameters keys = (RsaKeyParameters)privateKeyPemFile.ReadObject();

        OaepEncoding eng = new OaepEncoding(new RsaEngine());
        eng.Init(false, keys);

        int length = cipherTextBytes.Length;
        int blockSize = eng.GetInputBlockSize();
        List<byte> plainTextBytes = new List<byte>();
        for (int chunkPosition = 0;
            chunkPosition < length;
            chunkPosition += blockSize)
        {
            int chunkSize = Math.Min(blockSize, length - chunkPosition);
            plainTextBytes.AddRange(eng.ProcessBlock(
                cipherTextBytes, chunkPosition, chunkSize
            ));
        }

        string outString = Encoding.UTF8.GetString(plainTextBytes.ToArray());
        return outString;
    }

但是在尝试解密时失败,如下所示。

真正让我烦恼的是,如果我仅使用下面的加密方式使用 C# 进行加密和解密,它就可以工作。这是一个C#加密代码,使用openssl生成的.pem文件。

    static public string RSAEncrypt(string clearText)
    {
        byte[] plainTextBytes = Encoding.UTF8.GetBytes(clearText);

        RsaKeyParameters keys = (RsaKeyParameters)publicKeyPemFile.ReadObject();

        OaepEncoding eng = new OaepEncoding(new RsaEngine());
        eng.Init(true, keys);

        int length = plainTextBytes.Length;
        int blockSize = eng.GetInputBlockSize();
        List<byte> cipherTextBytes = new List<byte>();
        for (int chunkPosition = 0;
            chunkPosition < length;
            chunkPosition += blockSize)
        {
            int chunkSize = Math.Min(blockSize, length - chunkPosition);
            cipherTextBytes.AddRange(eng.ProcessBlock(
                plainTextBytes, chunkPosition, chunkSize
            ));
        }

        return result;Convert.ToBase64String(cipherTextBytes.ToArray());
    }

但是如果使用 openssl 加密并尝试使用 C# 解密总是崩溃。

最终测试程序

    static void Main(string[] args)
    {
        RSAEncryption.LoadKeys();
        RSAEncryption.TestRSAFromOpenSSL();
    }

    static public void TestRSAFromOpenSSL()
    {
        byte[] bytes = File.ReadAllBytes(filesRootPath + "crypt.bin");
        string outString = RSADecrypt(bytes);

        Debug.WriteLine("clear text = " + bytes.Length);
    }

有什么线索吗?

谢谢。

我尝试用一​​段更简单的代码来复制您的问题,但我做不到。但是在尝试解密文本时出现错误。事实证明,我的 openssl 使用 pkcs1 作为默认编码而不是 oaep。当我改变它时,我让它工作了。

这是我的代码

    byte[] cipherText = Convert.FromBase64String(File.ReadAllText("./crypt.bin"));
    PemReader pr = new PemReader((StreamReader)File.OpenText("./privkey.pem"));
    var keys = (RsaKeyParameters)pr.ReadObject();
    var eng = new Pkcs1Encoding(new RsaEngine());
    eng.Init(false, keys);

    var length = cipherText.Length;
    var blockSize = eng.GetInputBlockSize();
    var plainTextBytes = new List<byte>();
    for (int chunkPosition = 0; chunkPosition < length; chunkPosition += blockSize)
    {
        var chunkSize = Math.Min(blockSize, length - chunkPosition);
        plainTextBytes.AddRange(eng.ProcessBlock(cipherText, chunkPosition, chunkSize));
    }
    Console.WriteLine(Encoding.UTF8.GetString(plainTextBytes.ToArray()));

我 运行 在我的 Linux 盒子上通过 base64 编码加密文本,以减少 t运行 发送时出现任何错误的风险。我的代码使用 .NET core 3.1 和 Bouncy Castle Core 1.86。

您的代码中让我印象深刻的一件事是 LoadKeys 方法。它并没有真正加载密钥。它设置了一个可以加载密钥的 reader 。我的预感是,如果您两次请求同一个密钥,它将无法正常工作。你这样做吗?