c#中的非对称加密示例

本文关键字:加密 非对称 | 更新日期: 2023-09-27 18:09:29

我需要通过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;
        }
    }
}

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

c#中的非对称加密示例

正如您所注意到的,您是加密货币的初学者。如果这是一个学习加密的有趣的玩具项目,那就太好了。如果这是真正的产品代码,您将不安全地实现它。您应该使用现成的工具,如SSL/HTTPS/任何解决这个问题,而不是自己做错。

我要借此机会指出你的素描中致命的弱点。

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

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

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

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

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

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

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

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

作为回答发布,因为它对于评论来说太长了-尽管它并不是专门回答你的问题。

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

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

  • 步骤4和5是错误的:由于您在消息中发送了客户端的公钥,因此任何人都可以创建一个对并使用该对加密客户端标识。根据你的描述,服务器不知道客户端的密钥,这使得该签名除了确保消息的完整性之外什么也不做——具体来说,它不会以任何方式使客户端标识可信。

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

SSL基本上支持两种模式:

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

  • 也可以通过使用客户端证书实现双向认证。为了成功地协商连接,服务器必须直接知道并信任客户端证书或客户端证书的颁发者。在这个场景中,服务器确实知道客户端是谁,但是必须满足上面提到的先决条件。