客户端服务器加密通信(1):AES加密
2016-07-28 10:45
483 查看
1.为什么选择AES加密
AES加密属于对称加密。所谓对称加密,就是加密和解密用的是同一个KEY。对称加密速度快,适合日常使用。非对称加密就是加密和解密用的KEY是不一样的,安全性更高。虽然安全性更高,但是速度太慢,一般用来加密少量数据。DES加密的KEY是64位,而AES加密的KEY最低也是128位,还可以是192位,256位。所以加密强度比DES高。
2.实现思路
2.1 概述
客户端和服务器通信肯定是通过发送数据包来通信。我们在两方约定数据的格式,再按指定的格式进行通信,这就是通常所说的协议。为了方便使用,将数据包的创建,发送,接收封装起来,变成一个类,取个名字,叫做CMyPacket。2.2 类数据成员
class CMyPacket { private: int m_nLen; int m_nCmd; LPBYTE m_pData; static BYTE key[16]; enum { HEADER_SIZE = 8; } };
包中存在3个数据成员,下面我依次解释一下。
长度:数据包的总长度,就是m_pData的长度加上m_nCmd的4字节,加上m_nLen的4字节长。长度的存在是让接收方知道数据到底有多长。
命令:接收方查看这个值,根据不同的值,知道自己要产生什么样的行为,调用什么样的函数。
数据:根据命令的不同,数据的含义也不同。如果是下载文件,里面就是文件内容。也可以为空,表示没有任何东西。
key:一个16字节的数组就是128位,用来当作加密解密的key。
HEADER_SIZE:包里除了m_pData外的成员就是包头部,大小8字节。
2.3 类成员函数
包的行为一般有创建,销毁,发送,接收,所以我们至少得写这4个函数。class CMyPacket { public: CMyPacket(int nCmd, int nDataLen, LBPYTE pData); ~CMyPacket(); bool Send(SOCKET s); bool Recv(SOCKET s); };
创建:根据数据的长度和数据指针创建一个包。并且是拷贝数据到包里,而不是直接使用原来数据的指针。创建包的时候不给数据加密,到发送时再加密。
CMyPacket::CMyPacket(int nCmd, int nDataLen, LBPYTE pData) { if(nDataLen < 0) { m_nLen = 0 return; } m_nLen = nDataLen + HEADER_SIZE; m_nCmd = nCmd; m_pData = new BYTE[nDataLen]; if(m_pData == NULL) { m_nLen = 0 return; } memcpy(m_pData, pData, nDataLen); }
销毁:在创建时在堆里面申请了空间,在析构时就要释放这些空间,否则会造成内存泄漏。
CMyPacket::~CMyPacket() { if(m_pData != NULL) { delete[] m_pData; m_pData = NULL; //避免重复析构 } }
发送:发送时需要将创建好的包加密,并且发送时不止发送包里的数据成员,还要发送包加密前的长度,为什么呢?因为不足16字节的数据加密后的密文会变成16字节,这里就存在加密前后数据的长度差,如果不知道解密后的数据长度,那么解密后就无法确定明文数据的边界。
例:
对5字节的明文数据进行加密,实际上加密函数是要将这5字节数据填充到16字节后再进行加密,密文是16字节,将这16字节密文解密后,明文还是16字节。
明文数据 BYTE plain[5] = {1, 2, 3, 4, 5};
加密后数据 BYTE encrypted[16] = {xx, xx, xx, xx, …};
解密后数据 BYTE decrypted[16] = {1, 2, 3, 4, 5, 34, 45, 67, …};
光看解密后的数据,如果不知道长度,我们就会以为明文数据长度是16,但如果知道明文数据长度是5,我们就会只取前5字节,正确地还原出明文。
bool CMyPacket::Send(SOCKET s) { if(m_nLen == 0) { return false; } int nLen = m_nLen - 8; //Encrypt函数用AES key对数据进行加密,实现略 LPBYTE pEncrypted = Encrypt(nLen/*IN OUT*/, m_pData, key); //SendData函数循环发送data,直到发送完指定的长度,如果失败返回false,实现略 int nTotalLen = nLen + HEADER_SIZE; int nBeforeLen = m_nLen - HEADER-SIZE; nLen = m_nLen - HEADER-SIZE; if(SendData(s, 4/*data len*/, &nTotalLen/*data*/) && //总长度 SendData(s, 4/, &nBeforeLen) && //加密前数据长度 SendData(s, 4, &nCmd) && //命令 SendData(s, nLen, pEncrypted)) //加密后数据 { return true; } return false; }
接收:接收时按照发送的顺序接收,然后解密,如果接收出错,返回false。
bool CMyPacket::Recv(SOCKET s) { //RecvData函数循环接收data,直到接收完指定的长度,如果失败返回false,实现略 int nTotalLen = 0; int nBeforeLen = 0; int nCmd = 0; if(!RecvData(s, 4/*data len*/, &nTotalLen/*data*/) || //总长度 !RecvData(s, 4/, &nBeforeLen) || //加密前数据长度 !RecvData(s, 4, &nCmd) || //命令 { return false; } int nDataLen = nTotalLen - HEADER_SIZE; LPBYTE pData = new BYTE[nDataLen]; if(pData == NULL) { return false; } if(!RecvData(s, nDataLen, pData)) { return false; } int nLen = nDataLen; LPBYTE pDecrypted = Decrypt(nLen/*IN OUT*/, pData, key); delete[] pData; m_nLen = nTotalLen; m_nCmd = nCmd; pData = new BYTE[nBeforeLen]; memcpy(pData, pDecrypted, nBeforeLen); m_pData = pData; }
相关文章推荐
- 用ASP编写的加密和解密类
- C#实现子窗体与父窗体通信方法实例总结
- VBS脚本加密/解密VBS脚本(简易免杀版1.1)
- BAT加密工具 EncryBat 非编译型bat批处理加密方案与代码
- C#对称加密(AES加密)每次生成的结果都不同的实现思路和代码实例
- SQLServer 2008中的代码安全(一) 存储过程加密与安全上下文
- 实例讲解SQL Server加密功能
- C#实现对文件进行加密解密的方法
- C#实现数据包加密与解密实例详解
- C#最简单的字符串加密解密方法
- C#加密app.config中连接字符串的方法
- C#使用伪随机数实现加密用户密码的方法
- asp MD5加密方式使用建议
- C#对称加密与非对称加密实例
- java和c#使用hessian通信的方法
- win32下进程间通信(共享内存)实例分析
- 浅谈C#中Md5和Sha1两种加密方式
- 基于C#对用户密码使用MD5加密与解密
- vbs shellcode转换escape加密
- PHP加密解密字符串汇总