Je dois envoyer des données confidentielles à un serveur via une connexion TCP. J'ai fait beaucoup de recherches et je comprends la partie théorique. Sur la base de mes recherches, je veux faire ce qui suit :
Notez qu'il y a un serveur et un client : (nous supposons que les clés publiques du client ou du serveur peuvent être obtenues par n'importe qui).
-
le client crée sa clé publique et sa clé privée. Il peut chiffrer avec sa clé privée et déchiffrer avec sa clé publique.
-
Le serveur crée ses clés publique et privée. La clé privée est utilisée pour décrypter les messages et la clé publique pour les crypter. (remarque : c'est l'inverse pour le client).
-
le client obtient la clé publique du serveur. le client pourra alors crypter les messages avec cette clé et la seule personne qui pourra décrypter ce message sera la clé privée du serveur.
-
Comme le serveur doit être certain que le message provient de ce client spécifique, ce dernier crypte son nom (signature) à l'aide de sa clé privée.
-
le message du client contiendra donc : les données à envoyer, la clé publique du client, le nom du client crypté avec la clé privée du client.
-
le client crypte le message avec la clé publique du serveur. le client envoie ensuite ce message au serveur.
-
le serveur décryptera le message qu'il vient de recevoir avec sa clé privée.
-
une fois que le message est décrypté, il contient les données (informations), la signature cryptée et la clé publique du client.
-
enfin, le serveur décrypte la signature du client à l'aide de la clé publique contenue dans le message pour vérifier que le message provient bien de ce client.
Voici comment fonctionne la cryptographie asymétrique. J'ai également fait des recherches sur les classes qui vous permettent de créer ces paires de clés avec le cadre .NET. Les classes que j'ai recherchées et qui permettent de créer ces paires de clés publiques et privées sont les suivantes :
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
Le problème est donc de savoir comment utiliser une de ces classes pour le faire en C#. Je comprends comment fonctionne la partie théorique mais comment faire ce que je viens de décrire avec du code. J'ai cherché des exemples mais j'ai du mal à les comprendre.
Voici un exemple que j'ai trouvé et qui, selon moi, fait ce que j'ai décrit :
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();
}
}
}
}
Dans ce programme, je pense que le client est Alice et le serveur Bob. Je dois diviser ce programme en deux parties. J'ai du mal à le comprendre et si j'essaie, il est fort probable que je parvienne à le faire fonctionner. En tout cas, comment puis-je diviser ce programme en un code côté serveur et un code côté client. Je sais comment envoyer des octets entre le serveur et le client. Je n'ai pas envie de le faire fonctionner sans comprendre ce qui se passe.
EDITAR
J'ai réussi à séparer le code : voici le code du serveur (l'adresse IP de mon ordinateur est 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();
}
}
}
}
et voici le code client :
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;
}
}
}
Je pense qu'il est assez sûr. Chaque fois qu'il envoie un tableau d'octets différent, il envoie la même information !