【问题标题】:Asymmetric cryptography example in C#C# 中的非对称加密示例
【发布时间】:2011-11-24 07:05:26
【问题描述】:

我需要通过 TCP 连接将机密数据发送到服务器。我做了很多研究,我理解了理论部分。根据我的研究,我想做以下事情:

注意有服务器和客户端:(我们假设任何人都可以获取客户端或服务器的公钥)

  1. 客户端创建他的公钥和私钥。他能够用他的私钥加密并用他的公钥解密。

  2. 服务器创建他的公钥和私钥。私钥用于解密消息,公钥用于加密消息。 (注意与客户端相反)

  3. 客户端获取服务器的公钥。然后客户端将能够使用该密钥加密消息,而唯一能够解密该消息的将是服务器的私钥。

  4. 由于服务器需要确定消息来自该特定客户端,因此客户端将使用他的私钥加密他的姓名(签名)。

  5. 所以客户端消息将包含:要发送的数据、客户端的公钥、使用客户端私钥加密的客户端名称。

  6. 客户端将使用来自服务器的公钥加密消息。然后客户端会将该消息发送到服务器。

  7. 服务器将使用他的私钥解密它刚刚收到的消息。

  8. 一旦消息被解密,它将包含数据(信息)、加密签名、来自客户端的公钥。

  9. 最后,服务器将使用消息中包含的公钥解密客户端签名,以验证消息是否来自该客户端。


好的,这就是非对称加密的工作原理。我还研究了使您能够使用 .NET 框架创建此密钥对的类。我研究的能够让你创建这个公钥和私钥对的类是:

System.Security.Cryptography.DES
System.Security.Cryptography.DSACryptoServiceProvider 
System.Security.Cryptography.ECDsa 
System.Security.Cryptography.ECDsaCng 
System.Security.Cryptography.ECDiffieHellman 
System.Security.Cryptography.ECDiffieHellmanCng 
System.Security.Cryptography.RSA 
System.Security.Cryptography.RSACryptoServiceProvider 

所以现在我的问题是如何使用其中一个类来使用 C# 来完成它?我了解理论部分是如何工作的,但我该如何做我刚才用代码描述的事情。我研究了一些例子,但我很难理解它们。

这是我发现的一个例子,我相信它符合我所描述的:

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

namespace Example
{
    class Program
    {
        static CngKey aliceKey;
        static CngKey bobKey;
        static byte[] alicePubKeyBlob;
        static byte[] bobPubKeyBlob;

        static void Main()
        {
            CreateKeys();
            byte[] encrytpedData = AliceSendsData("secret message");
            BobReceivesData(encrytpedData);

            Console.Read();

        }

        private static void CreateKeys()
        {
            aliceKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
            bobKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
            alicePubKeyBlob = aliceKey.Export(CngKeyBlobFormat.EccPublicBlob);
            bobPubKeyBlob = bobKey.Export(CngKeyBlobFormat.EccPublicBlob);
        }

        private static byte[] AliceSendsData(string message)
        {
            Console.WriteLine("Alice sends message: {0}", message);
            byte[] rawData = Encoding.UTF8.GetBytes(message);
            byte[] encryptedData = null;

            using (var aliceAlgorithm = new ECDiffieHellmanCng(aliceKey))
            using (CngKey bobPubKey = CngKey.Import(bobPubKeyBlob,
                  CngKeyBlobFormat.EccPublicBlob))
            {
                byte[] symmKey = aliceAlgorithm.DeriveKeyMaterial(bobPubKey);
                Console.WriteLine("Alice creates this symmetric key with " +
                      "Bobs public key information: {0}",
                      Convert.ToBase64String(symmKey));

                using (var aes = new AesCryptoServiceProvider())
                {
                    aes.Key = symmKey;
                    aes.GenerateIV();
                    using (ICryptoTransform encryptor = aes.CreateEncryptor())
                    using (MemoryStream ms = new MemoryStream())
                    {
                        // create CryptoStream and encrypt data to send
                        var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);

                        // write initialization vector not encrypted
                        ms.Write(aes.IV, 0, aes.IV.Length);
                        cs.Write(rawData, 0, rawData.Length);
                        cs.Close();
                        encryptedData = ms.ToArray();
                    }
                    aes.Clear();
                }
            }
            Console.WriteLine("Alice: message is encrypted: {0}",
                  Convert.ToBase64String(encryptedData)); ;
            Console.WriteLine();
            return encryptedData;
        }

        private static void BobReceivesData(byte[] encryptedData)
        {
            Console.WriteLine("Bob receives encrypted data");
            byte[] rawData = null;

            var aes = new AesCryptoServiceProvider();

            int nBytes = aes.BlockSize >> 3;
            byte[] iv = new byte[nBytes];
            for (int i = 0; i < iv.Length; i++)
                iv[i] = encryptedData[i];

            using (var bobAlgorithm = new ECDiffieHellmanCng(bobKey))
            using (CngKey alicePubKey = CngKey.Import(alicePubKeyBlob,
                  CngKeyBlobFormat.EccPublicBlob))
            {
                byte[] symmKey = bobAlgorithm.DeriveKeyMaterial(alicePubKey);
                Console.WriteLine("Bob creates this symmetric key with " +
                      "Alices public key information: {0}",
                      Convert.ToBase64String(symmKey));

                aes.Key = symmKey;
                aes.IV = iv;

                using (ICryptoTransform decryptor = aes.CreateDecryptor())
                using (MemoryStream ms = new MemoryStream())
                {
                    var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write);
                    cs.Write(encryptedData, nBytes, encryptedData.Length - nBytes);
                    cs.Close();

                    rawData = ms.ToArray();

                    Console.WriteLine("Bob decrypts message to: {0}",
                          Encoding.UTF8.GetString(rawData));
                }
                aes.Clear();
            }
        }
    }
}

在这个程序中,我相信客户端是 Alice,服务器是 Bob。我必须把这个程序分成两部分。我很难理解它,如果我试一试,我很可能会成功。无论如何,我如何将该程序拆分为服务器端代码和客户端代码。我知道如何在服务器和客户端之间发送字节。但我不想在不了解发生了什么的情况下让它工作。也许你们可以给我看一个更简单的例子。


编辑

我设法将代码分开:这是服务器代码(我的计算机的IP地址恰好是192.168.0.120):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.IO;


namespace ServerListener
{
    class Program
    {
        static TcpListener server;


        //static CngKey aliceKey;
        static CngKey bobKey;
        static byte[] alicePubKeyBlob;
        static byte[] bobPubKeyBlob;

        static void Main(string[] args)
        {

            CreateKeys();

            IPAddress ipAddress = IPAddress.Parse("192.168.0.120");
            server = new TcpListener(ipAddress, 54540);
            server.Start();
            var client = server.AcceptTcpClient();
            var stream = client.GetStream();

            alicePubKeyBlob = new byte[bobPubKeyBlob.Length];
            stream.Read(alicePubKeyBlob, 0, alicePubKeyBlob.Length);

            stream.Write(bobPubKeyBlob, 0, bobPubKeyBlob.Length);

            byte[] encrytpedData = new byte[32];

            stream.Read(encrytpedData, 0, encrytpedData.Length);

            BobReceivesData(encrytpedData);


        }

        private static void CreateKeys()
        {
            //aliceKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
            bobKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
            //alicePubKeyBlob = aliceKey.Export(CngKeyBlobFormat.EccPublicBlob);
            bobPubKeyBlob = bobKey.Export(CngKeyBlobFormat.EccPublicBlob);
        }


        private static void BobReceivesData(byte[] encryptedData)
        {
            Console.WriteLine("Bob receives encrypted data");
            byte[] rawData = null;

            var aes = new AesCryptoServiceProvider();

            int nBytes = aes.BlockSize >> 3;
            byte[] iv = new byte[nBytes];
            for (int i = 0; i < iv.Length; i++)
                iv[i] = encryptedData[i];

            using (var bobAlgorithm = new ECDiffieHellmanCng(bobKey))
            using (CngKey alicePubKey = CngKey.Import(alicePubKeyBlob,
                  CngKeyBlobFormat.EccPublicBlob))
            {
                byte[] symmKey = bobAlgorithm.DeriveKeyMaterial(alicePubKey);
                Console.WriteLine("Bob creates this symmetric key with " +
                      "Alices public key information: {0}",
                      Convert.ToBase64String(symmKey));

                aes.Key = symmKey;
                aes.IV = iv;

                using (ICryptoTransform decryptor = aes.CreateDecryptor())
                using (MemoryStream ms = new MemoryStream())
                {
                    var cs = new CryptoStream(ms, decryptor, CryptoStreamMode.Write);
                    cs.Write(encryptedData, nBytes, encryptedData.Length - nBytes);
                    cs.Close();

                    rawData = ms.ToArray();

                    Console.WriteLine("Bob decrypts message to: {0}",
                          Encoding.UTF8.GetString(rawData));
                }
                aes.Clear();
            }
        }
    }
}

这是客户端代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Security.Cryptography;
using System.IO;

namespace ClientAlice
{
    class Program
    {
        static CngKey aliceKey;
        //static CngKey bobKey;
        static byte[] alicePubKeyBlob;
        static byte[] bobPubKeyBlob;

        static void Main(string[] args)
        {

            CreateKeys();
            bobPubKeyBlob = new byte[alicePubKeyBlob.Length];

            TcpClient alice = new TcpClient("192.168.0.120", 54540);

            var stream = alice.GetStream();
            stream.Write(alicePubKeyBlob, 0, alicePubKeyBlob.Length);

            stream.Read(bobPubKeyBlob, 0, bobPubKeyBlob.Length);


            byte[] encrytpedData = AliceSendsData(":)");

            stream.Write(encrytpedData, 0, encrytpedData.Length);


        }


        private static void CreateKeys()
        {
            aliceKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
            //bobKey = CngKey.Create(CngAlgorithm.ECDiffieHellmanP256);
            alicePubKeyBlob = aliceKey.Export(CngKeyBlobFormat.EccPublicBlob);
            //bobPubKeyBlob = bobKey.Export(CngKeyBlobFormat.EccPublicBlob);
        }

        private static byte[] AliceSendsData(string message)
        {
            Console.WriteLine("Alice sends message: {0}", message);
            byte[] rawData = Encoding.UTF8.GetBytes(message);
            byte[] encryptedData = null;

            using (var aliceAlgorithm = new ECDiffieHellmanCng(aliceKey))
            using (CngKey bobPubKey = CngKey.Import(bobPubKeyBlob,
                  CngKeyBlobFormat.EccPublicBlob))
            {
                byte[] symmKey = aliceAlgorithm.DeriveKeyMaterial(bobPubKey);
                Console.WriteLine("Alice creates this symmetric key with " +
                      "Bobs public key information: {0}",
                      Convert.ToBase64String(symmKey));

                using (var aes = new AesCryptoServiceProvider())
                {
                    aes.Key = symmKey;
                    aes.GenerateIV();
                    using (ICryptoTransform encryptor = aes.CreateEncryptor())
                    using (MemoryStream ms = new MemoryStream())
                    {
                        // create CryptoStream and encrypt data to send
                        var cs = new CryptoStream(ms, encryptor, CryptoStreamMode.Write);

                        // write initialization vector not encrypted
                        ms.Write(aes.IV, 0, aes.IV.Length);
                        cs.Write(rawData, 0, rawData.Length);
                        cs.Close();
                        encryptedData = ms.ToArray();
                    }
                    aes.Clear();
                }
            }
            Console.WriteLine("Alice: message is encrypted: {0}",
                  Convert.ToBase64String(encryptedData)); ;
            Console.WriteLine();
            return encryptedData;
        }
    }
}

我认为它非常安全。每次它发送一个不同的字节数组虽然发送相同的信息!

【问题讨论】:

  • 听起来您正在重新发明 SSL(或者至少,您的任务可以使用 SSL 完成)。您不想使用 SSL 的任何原因?
  • 我不知道知道如何在代码中使用私钥和公钥会很好,而不仅仅是理解理论部分......也许你是对的我应该使用 ssl 但我从未使用过那个伊瑟。我是密码学世界的新手……
  • 我不希望你们修改那个例子。有一个更时髦的例子会很好。我发布该示例只是为了向您展示我在寻找什么。我只是想了解它是如何工作的,并使其与代码一起工作......
  • 投票关闭它,因为它实际上并没有提出问题。也许代码可以发布在代码审查网站上。但就目前而言,唯一可能的答案是完全解释非对称密码学(包括如何正确拼写)和传输安全性。
  • 我的意思是:“第 1 步:客户创建他的公钥和私钥。他可以用他的私钥加密并用他的公钥解密。”太好了,这已经完全错误了。 请勿以上述文字为例。

标签: c# encryption cryptography


【解决方案1】:

如您所述,您是加密货币的初学者。如果这是一个有趣的玩具项目来了解加密,那就太好了。如果这是真正的生产代码你将不安全地实现它。您应该使用现成的工具(例如 SSL/HTTPS/其他工具)来解决此问题,而不是自己做错事。

我会借此机会指出你的草图存在致命弱点的地方。

3) 客户端获取服务器的公钥。

好的。如何? 这是最重要的一步。整个系统的安全都依赖于这一步,而你已经完全掩盖了它是如何工作的。 客户端如何获取服务器的公钥? 是什么阻止了一个邪恶的人打电话给客户端并说“嘿客户端,我是服务器。这是我的公钥!”现在客户端正在加密只能由作恶者解密的消息。作恶者拥有真实服务器的公钥,所以作恶者用真实的公钥重新加密消息并继续发送。您的整个系统因此受到损害。只有在存在安全的密钥交换机制时,公钥密码系统才是安全的。 (那么一个合理的问题是:如果你有一个安全的密钥交换机制,为什么不首先简单地使用它来交换消息呢?)

4) 由于服务器需要确定消息来自该特定客户端,因此客户端将使用他的私钥加密他的名字(签名)。

客户端应该加密整个消息的哈希作为签名,而不仅仅是消息的一部分。这样,服务器就有证据表明整个消息来自客户端。

6) 客户端将使用来自服务器的公钥加密消息。然后客户端会将该消息发送到服务器。

效率极低。更好的是让服务器和客户端就对称密码系统的密钥达成一致。可以使用公钥密码系统在服务器和客户端之间传输密钥。服务器和客户端现在有一个共享密钥,可用于此通信会话。

9) 最后,服务器将使用消息中包含的公钥解密客户端签名,以验证消息是否来自该客户端。

这到底有什么帮助?我想给你发消息。你想知道它来自谁。所以我给你发了一份我的驾照复印件,这样你就可以比较驾照上的签名和信息上的签名。你怎么知道我寄给你的是我的驾照而不是别人的复印件? 这根本不能解决客户端身份验证问题。同样,您需要解决密钥分发问题。系统依赖于有一个安全的密钥分发基础设施,它你没有指定。

【讨论】:

    【解决方案2】:

    作为答案发布,因为评论太长了 - 但它并没有专门回答你的问题。

    正如 driis 在评论中提到的,您应该真正依赖被认为是安全的现有解决方案。也就是说,您的协议确实存在安全问题:

    • 通信通常是双向的,但您似乎只处理单向通信(客户端到服务器)。这没有多大意义,因为你说你要使用 TCP,它本身就是一个双向协议。

    • 第 4 步和第 5 步有问题:由于您在消息中发送客户端的公钥,因此任何人都可以创建一对并使用该对加密客户端标识。根据您的描述,服务器没有客户端密钥的前向知识,这使得该签名除了确保消息的完整性之外什么都不做 - 特别是不会以任何方式使客户端标识值得信赖。

    为了正确识别,您确实有其他先决条件;服务器必须提前知道客户端的公钥,或者它必须能够通过使用受信任的第 3 方来相信客户端声称是他自己。这就是证书和证书信任链的意义所在:如果该客户端提供了由第 3 方 X 颁发的证书并且服务器信任 X,那么他可以假设该客户端就是他伪装的那个人。

    SSL基本上支持两种模式:

    • 要么只验证服务器身份,并且任何客户端都可以与之通信;客户端的身份没有经过验证,只是(在协商连接之后)它始终是与服务器通信的同一个客户端。这是在线购物等的典型用法 - 您(作为客户端)信任服务器并创建可信连接,但服务器不知道您是谁。

    • 也可以使用客户端证书进行双向身份验证。服务器必须直接知道并信任客户端证书或客户端证书的颁发者才能成功协商连接。在这种情况下,服务器确实知道客户端是谁,但必须满足上述先决条件。

    【讨论】:

      猜你喜欢
      • 2012-07-09
      • 2011-04-05
      • 2019-07-29
      • 1970-01-01
      • 2023-04-03
      • 2010-10-30
      • 2017-02-22
      • 2011-03-01
      • 1970-01-01
      相关资源
      最近更新 更多