以安全的方式在服务器和客户端之间交换对称密钥

时间:2011-09-24 17:07:52

标签: c# cryptography encryption-asymmetric

我有一个我认为安全的算法。因此,服务器为每个新连接创建公钥和私钥组合。在这个例子中,我只处理一个线程,但实际上我将不得不构造几个线程,以便服务器可以监听多个连接。

客户端是Alice,它还会创建公钥和私钥组合。这里是客户端Alice如何创建一个随机对称密钥以安全地提供给服务器bob。

服务器代码(Bob):

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 bobKey;          // server private key
        static byte[] alicePubKeyBlob; // client public key
        static byte[] bobPubKeyBlob;   // server public key

        static byte[] symetricKey;     // the symetric key that will later be used to transfer data more efficeintly

        static void Main(string[] args)
        {

            // create server private and public keys
            CreateKeys(); 

            // start listening for new connections
            IPAddress ipAddress = IPAddress.Parse("192.168.0.120");
            server = new TcpListener(ipAddress, 54540);
            server.Start();
            var client = server.AcceptTcpClient();

            // once a connection is established open the stream
            var stream = client.GetStream();

            // we need the client public key so we need to instantiate it.
            alicePubKeyBlob = new byte[bobPubKeyBlob.Length];

            // waint until the client send us his public key
            stream.Read(alicePubKeyBlob, 0, alicePubKeyBlob.Length);

            // alicePubKeyBlob should now be the client's public key

            // now let's send this servers public key to the client
            stream.Write(bobPubKeyBlob, 0, bobPubKeyBlob.Length);

            // encrytpedData will be the data that server will recive encrypted from the client with the server's public key
            byte[] encrytpedData = new byte[1024];
            // wait until client sends that data
            stream.Read(encrytpedData, 0, encrytpedData.Length);

            // decrypt the symetric key with the private key of the server
            symetricKey = BobReceivesData(encrytpedData);

            // server and client should know have the same symetric key in order to send data more efficently and securely
            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[] 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();

                return rawData;
            }
        }
    }
}

客户端代码(Alice):

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;  // client private key

        static byte[] alicePubKeyBlob; // client public key
        static byte[] bobPubKeyBlob;   // server public key
        static byte[] symetricKey;     // the symetric key that we want to give to the server securely

        static void Main(string[] args)
        {
            //create the client private and public keys
            CreateKeys();

            // initialice the server's public key we will need it later
            bobPubKeyBlob = new byte[alicePubKeyBlob.Length];

            // connect to the server and open a stream in order to comunicate with it
            TcpClient alice = new TcpClient("192.168.0.120", 54540);
            var stream = alice.GetStream();

            // send to the server the public key (client's public key)
            stream.Write(alicePubKeyBlob, 0, alicePubKeyBlob.Length);

            // now wait to receive the server's public key
            stream.Read(bobPubKeyBlob, 0, bobPubKeyBlob.Length);

            // create a random symetric key
            symetricKey = new byte[1000];
            Random r = new Random();
            r.NextBytes(symetricKey);

            // Encrypt the symetric key with the server's public key
            byte[] encrytpedData = AliceSendsData(symetricKey);

            // once encrypted send that encrypted data to the server. The only one that is going to be able to unecrypt that will be the server
            stream.Write(encrytpedData, 0, encrytpedData.Length);

            // not the server and client should have the same symetric key
        }


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

        private static byte[] AliceSendsData(byte[] rawData)
        {

            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连接。我认为在创建这类程序时我们学到了很多东西。我想知道这种技术是否是Alice以安全的方式向Bob提供对称密钥的安全方式。

1 个答案:

答案 0 :(得分:6)

您的代码受man-in-the-middle attacks的约束。 SSL通过依赖受信任的第三方并通过检查证书链到受信任的根来验证公钥来解决此问题。因此,您最好的选择是(a)采用SSL并使用它,(b)通过阅读安全书(首先)而不是通过实现自己的算法来学习。