Asymmetric cryptography example in C #

I need to send sensitive data to the server through a TCP connection. I have researched a lot, and I understand the theoretical part. Based on what I researched, I want to do the following:

Please note that there is a server and a client: (we assume that the public keys of the client or server can be obtained by anyone)

  • Client
  • creates his public and private key. It can encrypt with its private key and decrypt with its public key.

  • the server creates its public and private keys. the private key is used to decrypt messages, and the public key is used to encrypt messages. (pay attention to another way, as with the client)

  • the client receives the server’s public key. the client will then be able to encrypt messages with this key, and the only one that can decrypt this message will be the server’s private key.

  • since the server must be sure that the message is coming from this particular client, then the client will encrypt its name (signature) with its private key.

  • therefore, the client’s message will contain: data to be sent, client’s public key, client’s name, encrypted using the client’s private key.

  • the client will encrypt the public key message from the server. the client will send this message to the server.

  • the server decrypts the message just received with its private key.

  • After decrypting the message, it will contain data (information), an encrypted signature, a public key from the client.

  • finally, the server will decrypt the client’s signature with the public key that was contained in the message to ensure that the message is from this client.


OK, this is how asymmetric cryptography works. I also studied classes that allow you to create these key pairs using the .NET platform. The classes that I explored that allow you to create these public and private key pairs are as follows:

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 

So now there are problems, how to use one of these classes for this with C #? I understand how the theoretical part works, but how can I do what I just described using code. I have studied some examples, but it is difficult for me to understand 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(); } } } } 

In this program, I believe that the client is Alice, and the server is Bob. I need to split this program into two parts. It’s hard for me to understand this, and if I try, most likely I will make it work. In any case, how can I split this program into server-side code and client-side code. I know how to send bytes between server and client. But I do not want it to work, not understanding what is happening. maybe you guys can show me a simpler example.


EDIT

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

and here is the client code:

 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 think this is pretty safe. Each time it sends another array of bytes, sending the same information!

+6
source share
2 answers

As you noticed, you are new to cryptography. If this is a fun toy project to learn about cryptography, great. If this is real production code, you are not going to implement it safely. You should use ready-made tools such as SSL / HTTPS / to solve this problem, and not do it wrong.

I will take this opportunity to indicate areas where your sketch is deadly weak.

3) the client receives the public key of the server.

OK How? This is the most important step. The security of the entire system depends on this step, and you have completely disguised how it works. How does the client get the public key of the server? What prevents an evil person from calling a client and saying "hey client, I'm a server." Here is my public key! "And now the client encrypts messages that can only be decrypted by the attacker. Evildoer has a real server public key, so evildoer re-encrypts the message with the public key and sends it. Thus, your entire system is compromised. The public key cryptosystem is only protected in the event that there is a secure key exchange mechanism. (And then a reasonable question arises: if you have a secure key exchange mechanism, why not just use it for messaging and first of all?)

4), since the server must be sure that the message comes from this particular client, then the client will encrypt its name (signature) with its private key.

The client must encrypt the hash of the entire message as a signature, not just a part of the message. Thus, the server has evidence that the entire message was sent by the client.

6) the client will encrypt the public key message from the server. the client will send this message to the server.

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

9) finally, the server decrypts the client’s signature with the public key contained in the message in order to verify that the message is from this client.

How does this help? I want to send you a message. You want to know who it comes from. Therefore, I am sending you a photocopy of the license for my drivers, so you can compare the license signature with the signature in the message. How do you know that I sent you my license and not a photocopy of someone else's? This does not solve the client authentication problem at all. Again, you need to solve the key distribution problem. . The system depends on having a secure key distribution infrastructure that you did not specify.

+36
source

Posting as an answer, as it is too long for comment - it does not specifically answer your question.

As mentioned in a driis comment, you should really rely on existing solutions that are considered safe. However, your protocol has security issues:

  • Communication is usually two-way, but you seem to be referring to one-way communication (client to server). This does not make much sense, because you say that you intend to use TCP, which in itself is a two-way protocol.

  • Steps 4 and 5 are wrong: since you send the client’s public key inside the message, anyone can create a pair and encrypt the client’s identity using this pair. From your description, the server does not have direct knowledge of the client keys, which makes this signature nothing but ensure the integrity of the message - in particular, in no way makes the client identification reliable.

For proper identification, you have additional prerequisites; the server must know the client’s public key in advance, or it must be able to trust the client’s request to be itself using a trusted third party. Here is what certificates and certificate trust chains are: if this client presents a certificate issued by a third-party X, and the server trusts X, then he can assume that the client is the one he pretends to be.

SSL basically supports two modes:

  • Or only the server identifier is checked, and any client can communicate with it; client identification is not verified, only this (after the connection has been agreed), it is always the same client that communicates with the server. This is a typical use for online shopping, etc. - you (as a client) trust the server and create a trusted connection, but the server does not know who you are.

  • Two-way authentication can be performed using client certificates. The server must know and trust either the client certificate directly or the issuer of the client certificate in order to successfully negotiate the connection. In this case, the server really knows who the client is, but the prerequisite mentioned above must be met.

+4
source

Source: https://habr.com/ru/post/897971/


All Articles