非对称加密包装器

Asymmetric encryption wrapper

我写了一个非对称加密包装器如下:

using System;
using System.IO;
using System.Security.Cryptography;
using System.Text.Json;

namespace MyEncryptionLibrary
{
    public static class AsymmetricCipher
    {
        //Options for serialising the data. This will make it as small as possible:
        private static JsonSerializerOptions jsonSerialiserOptions = new JsonSerializerOptions
        {
            DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
        };

        public static string Encrypt<T>(T value, RSACryptoServiceProvider destinationPublicKeyRsa)
        {
            if (value == null)
            {
                return null;
            }

            try
            {
                var json = JsonSerializer.Serialize(value, jsonSerialiserOptions);

                using (var aesAlg = Aes.Create())
                {
                    using (var encryptor = aesAlg.CreateEncryptor())
                    {
                        using (var msEncrypt = new MemoryStream())
                        {
                            using (var csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))
                            using (var swEncrypt = new StreamWriter(csEncrypt))
                            {
                                swEncrypt.Write(json);
                            }

                            //Encrypt the key asymmetrically:
                            var encryptedKey = destinationPublicKeyRsa.Encrypt(aesAlg.Key, true);
                            var decryptedContent = msEncrypt.ToArray();

                            using (var ms = new MemoryStream())
                            using (var bW = new BinaryWriter(ms))
                            {
                                bW.Write(encryptedKey.Length);
                                bW.Write(encryptedKey);

                                bW.Write(aesAlg.IV.Length);
                                bW.Write(aesAlg.IV);

                                bW.Write(decryptedContent.Length);
                                bW.Write(decryptedContent);

                                return Convert.ToBase64String(ms.ToArray());
                            }
                        }
                    }
                }
            }
            catch
            {
                return null;
            }
        }

        public static T Decrypt<T>(string value, RSACryptoServiceProvider receiverPrivateKeyRsa)
        {
            T result = default(T);

            if (string.IsNullOrEmpty(value))
            {
                return result;
            }

            try
            {
                var fullCipher = Convert.FromBase64String(value);

                using (var ms = new MemoryStream(fullCipher))
                using (var bR = new BinaryReader(ms))
                {
                    var encryptedKeyLength = bR.ReadInt32();
                    var encryptedKey = bR.ReadBytes(encryptedKeyLength);

                    var ivLength = bR.ReadInt32();
                    var iv = bR.ReadBytes(ivLength);

                    var cipherLength = bR.ReadInt32();
                    var cipher = bR.ReadBytes(cipherLength);

                    //Decrypt the asymmetrically encrypted key:
                    var key = receiverPrivateKeyRsa.Decrypt(encryptedKey, true);

                    using (var aesAlg = Aes.Create())
                    {
                        using (var decryptor = aesAlg.CreateDecryptor(key, iv))
                        {
                            using (var msDecrypt = new MemoryStream(cipher))
                            {
                                using (var csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))
                                {
                                    using (var srDecrypt = new StreamReader(csDecrypt))
                                    {
                                        var json = srDecrypt.ReadToEnd();

                                        result = JsonSerializer.Deserialize<T>(json);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            catch { }

            return result;
        }
    }
}

为了测试,我生成了一些密钥,如下所示:

public class KeyGenerator
{
    private RSA rsa;

    public KeyGenerator()
    {
        rsa = RSA.Create();
    }

    public string GetPrivateKeyContainer()
    {
        return rsa.ToXmlString(true);
    }

    public string GetPublicKeyContainer()
    {
        return rsa.ToXmlString(false);
    }
}

然后通过以下方式在测试中使用它们:

    [TestInitialize]
    public void Initialise()
    {
        keyGenerator = new KeyGenerator();

        var publicKey = keyGenerator.GetPublicKeyContainer();
        var privateKey = keyGenerator.GetPrivateKeyContainer();

        publicKeyRsa = new RSACryptoServiceProvider();
        publicKeyRsa.FromXmlString(publicKey);

        privateKeyRsa = new RSACryptoServiceProvider();
        privateKeyRsa.FromXmlString(privateKey);
    }

我已经为它编写了一些单元测试,它似乎对大多数单元测试都运行良好。但是,以下测试让我感到困惑:

    [TestMethod]
    public void ItShouldWork_006()
    {
        //Here the private key is used in both directions:

        var text = "I wrote this. It should not matter how long it is as I may be sending a large string, or a smaller string, it's hard to say.";

        var encrpyted = AsymmetricCipher.Encrypt(text, privateKeyRsa);

        var decrypted = AsymmetricCipher.Decrypt<string>(encrpyted, privateKeyRsa);

        Assert.AreNotEqual(text, decrypted);
    }

此测试失败,因为解密的文本与输入文本匹配。我原以为用私钥加密的东西也不能用私钥解密,但似乎可以。这是因为私钥容器包含私钥和 public 密钥吗?还是我做错了什么?

非常感谢任何建议!

RSA.GetXmlString(bool) 方法的文档对此非常清楚:

The ToXmlString method creates an XML string that contains either the public and private key of the current RSA object or contains only the public key of the current RSA object.

事实上,RSA 对象上提取私钥的所有方法(ExportRSAPrivateKeyTryExportEncryptedPkcs8PrivateKey 等等)都是作为 public/private 密钥执行的一对。加密始终使用密钥对中的 public 完成,这就是正确保护连接的两端都有自己的密钥对的原因。 (关于原因的文章堆积如山,我真正理解的只有一小部分——加密很奇怪。)

原则上可以将 public 和私钥分开,但不能使用标准 RSA class。