非对称加密包装器

发布于 2025-01-11 00:36:45 字数 5912 浏览 0 评论 0原文

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

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);
    }

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

非常感谢任何建议!

I have written an asymmetric encryption wrapper as follows:

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;
        }
    }
}

For my testing, I generated some keys as follows:

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);
    }
}

And then used them in the test via:

    [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);
    }

I've written some unit tests for it, and it seems to work well for most of them. However, the following test is confusing me:

    [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);
    }

This test fails because the decrypted text matches the input text. I would have expected that something encrpted with the private key could not also be decrypted with the private key, but it seems that it can. Is this because the private key container contains both the private and public keys? Or am I doing something wrong?

Any advice is much appreciated!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

已下线请稍等 2025-01-18 00:36:45

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

ToXmlString 方法创建一个 XML 字符串,其中包含当前 RSA 对象的公钥和私钥,或者仅包含当前 RSA 对象的公钥。

事实上,RSA 对象上提取私钥的所有方法(ExportRSAPrivateKeyTryExportEncryptedPkcs8PrivateKey 等)都是作为 public/私钥对。加密始终使用该对的公钥完成,这就是为什么正确安全的连接的两端都有自己的密钥对。 (关于原因的文章有很多,但我真正理解的只有一小部分 - 加密很奇怪。)

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

The documentation for the RSA.GetXmlString(bool) method is fairly clear on this:

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.

In fact all of the methods on the RSA object that extract the private key (ExportRSAPrivateKey, TryExportEncryptedPkcs8PrivateKey and so on) do so as a public/private key pair. Encryption is always done with the public key of the pair, which is why both ends of a properly secured connection have their own key pairs. (There are mountains of writings on why, only a tiny portion of which I actually understand - encryption is weird.)

In principle it's possible to separate the public and private keys, but not with the standard RSA class.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文