本文介绍了C# 中的非对称加密示例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I need to send confidential data to a server over a TCP connection. I have done a lot of researching and I understand the theoretical part. Based on what I have researched I want to do the following:

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

Note there is a server and a client: (we assume that public keys of either the client or server can be obtain by anyone)

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

  1. client creates his public and private key. He is able to encrypt with his private key and decrypt with his public key.

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

server creates his public and private keys. private key is used to decrypt messages and public key is used to encrypt messages. (note is the other way around as with the client)

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

the client get's the server's public key. client then will be able to encrypt messages with that key and the only one that will be able to decrypt that message would be the server's private key.

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

since the server needs to be certain that the message comes from that specific client then the client will encrypt his name (signature) with his private key.

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

so the client message will contain: data to be send, client's public key, client name encrypted with the client's private key.

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

the client will encrypt the message with the public key from the server. client will then send that message to the server.

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

the server will decrypt the message it just received with his private key.

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

once the message is decrypted it will contain the data (info), encrypted signature, public key from client.

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

finally, the server will decrypt the client signature with the public key that was contained on the message to verify that the message is from that client.

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


OK so this is how asymmetric cryptography works. I have also researched about the classes that enable you to create this key pairs with the .NET framework. The classes that I researched that enable you do create this public and private key pairs are:

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# 来完成它?我了解理论部分是如何工作的,但我该如何做我刚才用代码描述的事情.我研究了一些例子,但我很难理解它们.

so now my problems comes on how do I use one of this classes to do it with C#? I understand how the theoretical part works but how do I do what I just described with code. I have researched for some examples but I am having a hard time understanding them.

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

here is one example that I found that I believe does what I described:

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

In this program I believe the client is Alice and the server is Bob. I have to split this program into two parts. I am having a hard time understanding it and if I give it a try most likely I will make it work. Anyways how can I split this program into a server side code and client side code. I know how to send bytes between server and client. But I don't want to make it work without understanding what is going on. maybe you guys can show me an easier example.

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

I managed to separate the code: here is the server code (the ip address of my computer happened to be 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;
        }
    }
}

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

I thinks it is pretty secure. Every time it sends a different byte array although sending the same info!

推荐答案

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

As you note, you are a beginner at crypto. If this is a fun toy project to learn about crypto, great. If this is real production code you are going to implement it insecurely. You should be using off-the-shelf tools like SSL/HTTPS/whatever to solve this problem rather than doing it wrong yourself.

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

I'll take this opportunity to point out areas where your sketch is fatally weak.

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

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

OK. How? This is the most important step. The security of the entire system relies upon this step, and you have completely glossed over how it works. How does the client obtain the public key of the server? What stops an evil person from calling up the client and saying "hey client, I'm the server. Here's my public key!" And now the client is encrypting messages that can only be decrypted by the evildoer. The evildoer has the real server's public key, so the evildoer re-encrypts the message with the real public key and sends it on. Your whole system is thereby compromised. The public key cryptosystem is only secure if there is a secure key exchange mechanism. (And a reasonable question then is: if you have a secure key exchange mechanism, why not simply use it to exchange the message in the first place?)

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

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

The client should encrypt a hash of the entire message as the signature, not just a part of the message. That way the server has evidence that the whole message was from the client.

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

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

This is extremely inefficient. Better is for the server and client to agree upon a key to a symmetric cryptosystem. The key can be transmitted between the server and the client using the public key cryptosystem. The server and client now have a shared secret key that they can use for this communication session.

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

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

How on earth does that help anything? I want to send you a message. You want to know who it comes from. So I send you a photocopy of my drivers license, so you can compare the signature on the license with the signature on the message. How do you know I sent you my drivers license and not a photocopy of someone else's? This doesn't solve the client authentication problem at all. Again, you need to solve the key distribution problem. The system depends on there being a secure key distribution infrastructure, which you have not specified.

这篇关于C# 中的非对称加密示例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 02:26