問題描述
我需要通過 TCP 連接將機(jī)密數(shù)據(jù)發(fā)送到服務(wù)器.我做了很多研究,我理解了理論部分.根據(jù)我的研究,我想做以下事情:
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:
注意有一個(gè)服務(wù)器和一個(gè)客戶端:(我們假設(shè)任何人都可以獲取客戶端或服務(wù)器的公鑰)
Note there is a server and a client: (we assume that public keys of either the client or server can be obtain by anyone)
客戶創(chuàng)建他的公鑰和私鑰.他能夠用他的私鑰加密并用他的公鑰解密.
client creates his public and private key. He is able to encrypt with his private key and decrypt with his public key.
服務(wù)器創(chuàng)建他的公鑰和私鑰.私鑰用于解密消息,公鑰用于加密消息.(注意與客戶端相反)
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)
客戶端獲取服務(wù)器的公鑰.然后,客戶端將能夠使用該密鑰加密消息,而唯一能夠解密該消息的將是服務(wù)器的私鑰.
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.
由于服務(wù)器需要確定消息來(lái)自該特定客戶端,因此客戶端將使用他的私鑰加密他的名字(簽名).
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.
因此客戶端消息將包含:要發(fā)送的數(shù)據(jù)、客戶端的公鑰、使用客戶端私鑰加密的客戶端名稱.
so the client message will contain: data to be send, client's public key, client name encrypted with the client's private key.
客戶端將使用來(lái)自服務(wù)器的公鑰加密消息.然后客戶端會(huì)將該消息發(fā)送到服務(wù)器.
the client will encrypt the message with the public key from the server. client will then send that message to the server.
服務(wù)器將使用他的私鑰解密它剛剛收到的消息.
the server will decrypt the message it just received with his private key.
一旦消息被解密,它將包含數(shù)據(jù)(信息)、加密簽名、來(lái)自客戶端的公鑰.
once the message is decrypted it will contain the data (info), encrypted signature, public key from client.
最后,服務(wù)器將使用消息中包含的公鑰解密客戶端簽名,以驗(yàn)證消息是否來(lái)自該客戶端.
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.
<小時(shí)>
好的,這就是非對(duì)稱加密的工作原理.我還研究了使您能夠使用 .NET 框架創(chuàng)建此密鑰對(duì)的類.我研究的能夠讓你創(chuàng)建這個(gè)公鑰和私鑰對(duì)的類是:
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
所以現(xiàn)在我的問題是如何使用其中一個(gè)類來(lái)使用 C# 來(lái)完成它?我了解理論部分是如何工作的,但我該如何做我剛才用代碼描述的事情.我研究了一些例子,但我很難理解它們.
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.
這是我發(fā)現(xiàn)的一個(gè)例子,我相信它符合我所描述的:
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();
}
}
}
}
在這個(gè)程序中,我相信客戶端是 Alice,服務(wù)器是 Bob.我必須把這個(gè)程序分成兩部分.我很難理解它,如果我試一試,我很可能會(huì)成功.無(wú)論如何,我如何將該程序拆分為服務(wù)器端代碼和客戶端代碼.我知道如何在服務(wù)器和客戶端之間發(fā)送字節(jié).但我不想在不了解發(fā)生了什么的情況下讓它工作.也許你們可以給我看一個(gè)更簡(jiǎn)單的例子.
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.
我設(shè)法將代碼分開:這是服務(wù)器代碼(我的計(jì)算機(jī)的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;
}
}
}
我認(rèn)為它非常安全.每次它發(fā)送一個(gè)不同的字節(jié)數(shù)組雖然發(fā)送相同的信息!
I thinks it is pretty secure. Every time it sends a different byte array although sending the same info!
推薦答案
如您所見,您是加密貨幣的初學(xué)者.如果這是一個(gè)有趣的玩具項(xiàng)目來(lái)了解加密,那就太好了.如果這是真正的生產(chǎn)代碼你將不安全地實(shí)現(xiàn)它.您應(yīng)該使用現(xiàn)成的工具(例如 SSL/HTTPS/其他工具)來(lái)解決此問題,而不是自己做錯(cuò)事.
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.
我會(huì)借此機(jī)會(huì)指出你的草圖存在致命弱點(diǎn)的地方.
I'll take this opportunity to point out areas where your sketch is fatally weak.
3) 客戶端獲取服務(wù)器的公鑰.
3) the client get's the server's public key.
好的.如何?這是最重要的一步. 整個(gè)系統(tǒng)的安全都依賴于這一步,而你完全掩蓋了它是如何工作的.客戶端如何獲取服務(wù)器的公鑰? 是什么阻止了一個(gè)邪惡的人打電話給客戶端并說(shuō)嘿客戶端,我是服務(wù)器.這是我的公鑰!"現(xiàn)在客戶端正在加密只能由作惡者解密的消息.作惡者擁有真實(shí)服務(wù)器的公鑰,所以作惡者用真實(shí)的公鑰重新加密消息并繼續(xù)發(fā)送.您的整個(gè)系統(tǒng)因此受到損害.只有在存在安全的密鑰交換機(jī)制時(shí),公鑰密碼系統(tǒng)才是安全的.(那么一個(gè)合理的問題是:如果您有一個(gè)安全的密鑰交換機(jī)制,為什么不首先簡(jiǎn)單地使用它來(lái)交換消息?)
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) 由于服務(wù)器需要確定消息來(lái)自該特定客戶端,因此客戶端將使用他的私鑰加密他的名字(簽名).
4) 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.
客戶端應(yīng)該加密整個(gè)消息的哈希作為簽名,而不僅僅是消息的一部分.這樣,服務(wù)器就有證據(jù)表明整個(gè)消息來(lái)自客戶端.
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) 客戶端將使用來(lái)自服務(wù)器的公鑰加密消息.然后客戶端會(huì)將該消息發(fā)送到服務(wù)器.
6) the client will encrypt the message with the public key from the server. client will then send that message to the server.
這效率極低.更好的是讓服務(wù)器和客戶端就對(duì)稱密碼系統(tǒng)的密鑰達(dá)成一致.可以使用公鑰密碼系統(tǒng)在服務(wù)器和客戶端之間傳輸密鑰.服務(wù)器和客戶端現(xiàn)在有一個(gè)共享密鑰,可用于此通信會(huì)話.
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) 最后,服務(wù)器將使用消息中包含的公鑰解密客戶端簽名,以驗(yàn)證消息是否來(lái)自該客戶端.
9) lastly, 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.
這到底有什么幫助?我想給你發(fā)消息.你想知道它來(lái)自誰(shuí).所以我給你發(fā)了一份我的駕照復(fù)印件,這樣你就可以比較駕照上的簽名和信息上的簽名.你怎么知道我寄給你的是我的駕照而不是別人的復(fù)印件?這根本不能解決客戶端身份驗(yàn)證問題.同樣,您需要解決密鑰分發(fā)問題.系統(tǒng)依賴于有一個(gè)安全的密鑰分發(fā)基礎(chǔ)設(shè)施,它你沒有指定.
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.
這篇關(guān)于C# 中的非對(duì)稱加密示例的文章就介紹到這了,希望我們推薦的答案對(duì)大家有所幫助,也希望大家多多支持html5模板網(wǎng)!