您的位置:首页 > 编程语言 > C#

编写高质量代码改善C#程序的157个建议——建议117:使用SSL确保通信中的数据安全

2015-08-22 20:53 295 查看
建议117:使用SSL确保通信中的数据安全

SSL(Secure Socket Layer)最初是由NetScape公司设计的,用于Web安全的网络协议。目前它已经广泛应用到各类网络传输通信中了。SSL利用数字证书技术(非对称加密),保证了通信过程中的唯一性、不可篡改性、不可抵赖性。SSL通道原理图:

public class RijndaelProcessor
{
static int bufferSize = 128 * 1024;
static byte[] salt = { 134, 216, 7, 36, 88, 164, 91, 227, 174, 76, 191, 197, 192, 154, 200, 248 };
static byte[] iv = { 134, 216, 7, 36, 88, 164, 91, 227, 174, 76, 191, 197, 192, 154, 200, 248 };

static SymmetricAlgorithm CreateRijndael(string password, byte[] salt)
{
PasswordDeriveBytes pdb = new PasswordDeriveBytes(password, salt, "SHA256", 1000);
SymmetricAlgorithm sma = Rijndael.Create();
sma.KeySize = 256;
sma.Key = pdb.GetBytes(32);
sma.Padding = PaddingMode.PKCS7;
return sma;
}

public static string EncryptString(string input, string password)
{
using (MemoryStream memoryStream = new MemoryStream())
using (SymmetricAlgorithm algorithm = CreateRijndael(password, salt))
{
algorithm.IV = iv;
using (CryptoStream cryptoStream = new CryptoStream(memoryStream, algorithm.CreateEncryptor(), CryptoStreamMode.Write))
{
byte[] bytes = UTF32Encoding.Default.GetBytes(input);
cryptoStream.Write(bytes, 0, bytes.Length);
cryptoStream.Flush();
}
return Convert.ToBase64String(memoryStream.ToArray());
}
}

public static string DencryptString(string input, string password)
{
using (MemoryStream inputMemoryStream = new MemoryStream(Convert.FromBase64String(input)))
using (SymmetricAlgorithm algorithm = CreateRijndael(password, salt))
{
algorithm.IV = iv;
using (CryptoStream cryptoStream = new CryptoStream(inputMemoryStream, algorithm.CreateDecryptor(), CryptoStreamMode.Read))
{
StreamReader sr = new StreamReader(cryptoStream);
return sr.ReadToEnd();
}
}
}
}


View Code

这是一WinForm窗体程序,模拟的是服务器端部分,其中有两个按钮。按钮事件方法buttonStartServe_Click负责让服务器处理侦听状态。当然,为了模拟需要,在方法中还初始化了非对称加密密钥对。记住,公钥要公开给客户端。注意,也可以使用数字证书,但是为了演示方便,本例仅使用公钥-私钥对。

通信部分直接使用了FCL中的Socket类型,并采用异步的方式处理发送和接收任务。关于通信部分,本建议不再赘述。唯一要注意的是,在发送和接收过程中,要调用RijndaelProcessor.EncryptString方法加密,然后用RijndaelProcessor.DencryptString方法解密。RijndaelProcessor类型是用来封装对称加密、解密算法的工具类。

客户端部分代码:

#region client
///======================
///客户端代码
///======================

///用于跟服务器通信的socket
Socket clientCommunicateSocket;
///用于暂存接收到的字符串
StringBuilder messageFromServer = new StringBuilder();
///定义接受缓存块的大小
static int clientBufferSize = 1024;
///缓存块
byte[] bytesReceivedFromServer = new byte[clientBufferSize];
//随机生成的key,在这里硬编码为key123
string keyCreateRandom = "key123";

private void buttonConnectAndReceiveMsg_Click(object sender, EventArgs e)
{
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("192.168.1.100"), 8009);
Socket connectSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
connectSocket.BeginConnect(iep, new AsyncCallback(this.Connected), connectSocket);
buttonConnectAndReceiveMsg.Enabled = false;
}

void Connected(IAsyncResult result)
{
clientCommunicateSocket = result.AsyncState as Socket;
clientCommunicateSocket.EndConnect(result);
clientCommunicateSocket.BeginReceive(bytesReceivedFromServer, 0, clientBufferSize, SocketFlags.None, new AsyncCallback(this.ReceivedFromServer), null);
ListBoxClientShow("客户端连接上服务器。。。");
//连接成功便发送密钥K给服务器
SendKey();
}

void ReceivedFromServer(IAsyncResult result)
{
int read = clientCommunicateSocket.EndReceive(result);
if (read > 0)
{
messageFromServer.Append(UTF32Encoding.Default.GetString(bytesReceivedFromServer, 0, read));
//处理并显示客户端数据
ProcessAndShowInClient();
clientCommunicateSocket.BeginReceive(bytesReceivedFromServer, 0, clientBufferSize, 0, new AsyncCallback(ReceivedFromServer), null);
}
}

private void ProcessAndShowInClient()
{
//如果接收到<EOF>则表示完成一次接收,否则继续将自己置于接收状态
if (messageFromServer.ToString().IndexOf("<EOF>") > -1)
{
//解密消息体并呈现出来
ListBoxClientShow(string.Format("接收到服务器消息:{0}", RijndaelProcessor.DencryptString(messageFromServer.ToString().Substring(0, messageFromServer.ToString().Length - 5), keyCreateRandom)));
messageFromServer.Clear();
}
}

private void buttonStartSendToServer_Click(object sender, EventArgs e)
{
//加密消息体
string msg = string.Format("{0}{1}", RijndaelProcessor.EncryptString(DateTime.Now.ToString(), keyCreateRandom), "<EOF>");
byte[] msgBytes = UTF32Encoding.Default.GetBytes(msg);
clientCommunicateSocket.BeginSend(msgBytes, 0, msgBytes.Length, SocketFlags.None, null, null);
ListBoxClientShow(string.Format("发送:{0}", msg));
}

private void SendKey()
{
string msg = RSAProcessor.RSAEncrypt(publicKey, keyCreateRandom) + "<KEY><EOF>";
byte[] msgBytes = UTF32Encoding.Default.GetBytes(msg);
clientCommunicateSocket.BeginSend(msgBytes, 0, msgBytes.Length, SocketFlags.None, null, null);
ListBoxClientShow(string.Format("发送:{0}", keyCreateRandom));
}

private void ListBoxClientShow(string msg)
{
listBoxClient.BeginInvoke(new Action(() =>
{
listBoxClient.Items.Add(msg);
}));
}
#endregion client


客户端部分也包含两个按钮,在服务器部分按下“侦听”按钮后,客户端可以按下“链接”按钮。这个过程,程序主要完成两件事情。首先,程序会根据服务器IP地址连接上服务器;其次,一旦连接上服务器,客户端会立即将自己用于加密的密钥发送给服务器。

完成这个步骤后,SSL通道已经建立起来的,这个时候就可以随意发送加密数据而不担心被盗走了。我们可以看到,客户端的代码与服务器端一样,在发送之前,消息要加密,而在接收到消息体之后,首先会解密。

转自:《编写高质量代码改善C#程序的157个建议》陆敏技
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: