我有一个我认为安全的算法。因此,服务器为每个新连接创建公钥和私钥组合。在这个例子中,我只处理一个线程,但实际上我将不得不构造几个线程,以便服务器可以监听多个连接。
客户端是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提供对称密钥的安全方式。
答案 0 :(得分:6)
您的代码受man-in-the-middle attacks的约束。 SSL通过依赖受信任的第三方并通过检查证书链到受信任的根来验证公钥来解决此问题。因此,您最好的选择是(a)采用SSL并使用它,(b)通过阅读安全书(首先)而不是通过实现自己的算法来学习。