您的位置:首页 > 其它

客户端服务器加密通信(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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  加密 通信