C#中的非对称加密示例

时间:2011-09-24 15:33:45

标签: c# encryption cryptography

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

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

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

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

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

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

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

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

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

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

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


  10. 好的,这就是非对称加密的工作原理。我还研究过使用.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;
            }
        }
    }
    

    我认为它非常安全。每次发送不同的字节数组,尽管发送相同的信息!

2 个答案:

答案 0 :(得分:37)

正如您所说,您是加密的初学者。如果这是一个有趣的玩具项目,了解加密,很棒。如果这是真正的生产代码,那么你将不安全地实现它。您应该使用现成的工具,如SSL / HTTPS /解决此问题,而不是自己做错。

我将借此机会指出你的草图致命弱点的区域。

  

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

行。怎么样? 这是最重要的一步。整个系统的安全性依赖于此步骤,您已经完全掩盖了它的工作原理。 客户端如何获取服务器的公钥?什么阻止邪恶的人打电话给客户并说“嘿客户端,我是服务器。这是我的公钥!”现在,客户端正在加密只能由恶人解密的消息。恶人拥有真实服务器的公钥,因此恶人用真实公钥重新加密消息并发送。因此,整个系统都受到了损害。如果存在安全密钥交换机制,则公钥密码系统仅是安全的。 (然后一个合理的问题是:如果你有一个安全的密钥交换机制,为什么不直接用它来交换消息?)

  

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

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

  

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

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

  

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

这对地球有什么帮助?我想给你发一条消息。你想知道它来自谁。因此,我向您发送了我的驾驶执照复印件,以便您可以将许可证上的签名与邮件上的签名进行比较。你怎么知道我发给你我的驾驶执照而不是别人的复印件? 这根本无法解决客户端身份验证问题。再次,您需要解决密钥分发问题。系统依赖于存在安全密钥分发基础结构,你还没有说明。

答案 1 :(得分:4)

作为答案发布,因为评论时间太长 - 但它并没有具体回答你的问题。

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

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

  • 步骤4和5是错误的:因为您在邮件中发送客户端的公钥,所以任何人都可以创建一对并使用该对加密客户端标识。根据您的描述,服务器没有客户端密钥的前瞻性知识,这使得此签名不会做任何事情,只能确保消息的完整性 - 特别是不会以任何方式使客户端标识值得信任。

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

SSL基本上支持两种模式:

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

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