这个代码用于登录服务器。
第一步:客户端生成一对公钥和私钥,并将客户端公钥发给服务器。
第二步:服务器生成一对公钥和密钥,并将服务器公钥发给客户端。
第三步:客户端使用服务器公钥给登录账号密码加密发给服务器,服务器使用服务器私钥解密,并校验账号密码的正确性。
第四步:如果密码正确,服务器读取数据库中的用户信息,使用客户端的公钥加密,发给客户端
第五步:客户端使用客户端私钥将用户信息解密。
一 生成密钥和加解密算法的封装
头文件
#include <string>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <openssl/bn.h> // this is for the BN_new
/**
* @todo:这里RSA的封装是PEM方式,在创建公钥-私钥时需要保存到文件再从文件中读取。
* 下一个版本要改进这里,避免磁盘操作。
*/
class CRsaPeer
{
public:
// 打开一对公钥私钥
// 需要加密时,必须指定公钥文件。公钥文件不存在时加密函数会失败。
// 需要解密时,必须制定私钥文件。私钥文件不存在时解密函数会失败。
explicit CRsaPeer(const std::string& strPubKeyFilePath ,
const std::string& strPriKeyPath,
const std::string& strPrivateFilePassword);
~CRsaPeer();
// 创建一对公钥-私钥
static CRsaPeer* CreateRsaFiles(
const std::string& strPubKeyFilePath ,
const std::string& strPriKeyPath,
const std::string& strPrivateFilePassword
);
public:
std::string GetPubKeyPath() const { return m_strPubKeyPath; }
std::string GetPriKeyPath() const { return m_strPriKeyPath; }
// 打开m_strPubKeyPath指定的文件,读取公钥。
int OpenPublicKey();
// 打开m_strPriKeyPath指定的文件,解密并读取私钥
int OpenPrivateKey();
// 加密内容
int Encrypt(
const unsigned char *orig_data,
size_t orig_data_len,
unsigned char *enc_data,
size_t &enc_data_len
);
// 解密内容
int Decrypt(
const unsigned char *enc_data,
size_t enc_data_len,
unsigned char *orig_data,
size_t &orig_data_len);
private:
// 创建公钥和私钥文件。
int CreateKeyPairFileInternal();
// 禁止拷贝构造和赋值
CRsaPeer(const CRsaPeer& r);
CRsaPeer operator = (const CRsaPeer& r);
private:
// 公钥存放的路径
std::string m_strPubKeyPath;
// 私钥存放的路径
std::string m_strPriKeyPath;
// 私钥的加密密码
std::string m_strPasswordForPrivateKey;
// 公钥
EVP_PKEY* m_pPubKey;
// 私钥
EVP_PKEY* m_pPriKey;
};
实现:
#include "stdafx.h"
#include "PackSSL.h"
#include "../common/TString.h"
using std::string;
CRsaPeer::CRsaPeer( const string& strPubKeyPath ,
const string& strPriKeyPath ,
const string& strPswdForPrivateKey)
{
m_pPubKey = NULL ;
m_pPriKey = NULL ;
if ( strPubKeyPath.empty () && strPriKeyPath.empty() )
{
perror("file stores key values empty");
return;
}
if ( strPswdForPrivateKey.empty ())
{
perror("password empty , use default");
m_strPasswordForPrivateKey = "Wehavetowork8daysperweekbutonlYpayed1.0$";
}
printf("here ");
m_strPubKeyPath = strPubKeyPath ;
m_strPriKeyPath = strPriKeyPath ;
m_strPasswordForPrivateKey = strPswdForPrivateKey ;
}
CRsaPeer::~CRsaPeer ()
{
if ( m_pPubKey )
EVP_PKEY_free( m_pPubKey );
if ( m_pPriKey )
EVP_PKEY_free( m_pPriKey );
}
CRsaPeer* CRsaPeer::CreateRsaFiles(
const std::string& strPubKeyFilePath ,
const std::string& strPriKeyPath,
const std::string& strPrivateFilePassword
)
{
CRsaPeer *RetValue = new CRsaPeer(strPubKeyFilePath,strPriKeyPath,strPrivateFilePassword);
if (0 != RetValue->CreateKeyPairFileInternal())
{
delete RetValue;
return NULL;
}
return RetValue;
}
// 打开公钥文件,返回EVP_PKEY结构的指针
// 返回0表示成功,其他值表示错误。
int CRsaPeer::OpenPublicKey()
{
if (m_pPubKey)
{
return 0;
}
RSA *rsa = NULL;
OpenSSL_add_all_algorithms();
BIO *bp = BIO_new(BIO_s_file());;
BIO_read_filename(bp, m_strPubKeyPath.c_str());
if(NULL == bp)
{
printf("open_public_key bio file new error!\n");
return 1;
}
rsa = PEM_read_bio_RSAPublicKey(bp, NULL, NULL, NULL);
if(rsa == NULL)
{
printf("open_public_key failed to PEM_read_bio_RSAPublicKey!\n");
BIO_free(bp);
RSA_free(rsa);
return 2;
}
printf("open_public_key success to PEM_read_bio_RSAPublicKey!\n");
m_pPubKey = EVP_PKEY_new();
if(NULL == m_pPubKey)
{
printf("open_public_key EVP_PKEY_new failed\n");
RSA_free(rsa);
return 3;
}
EVP_PKEY_assign_RSA(m_pPubKey, rsa);
return 0;
}
// 打开私钥文件,返回EVP_PKEY结构的指针
int CRsaPeer::OpenPrivateKey()
{
if (m_pPriKey)
{
return 0;
}
RSA *rsa = RSA_new();
OpenSSL_add_all_algorithms();
BIO *bp = NULL;
bp = BIO_new_file(m_strPriKeyPath.c_str(), "rb");
if(NULL == bp)
{
printf("open_private_key bio file new error!\n");
return 1;
}
rsa = PEM_read_bio_RSAPrivateKey(bp, &rsa, NULL, (void *)m_strPasswordForPrivateKey.c_str());
if(rsa == NULL)
{
printf("open_private_key failed to PEM_read_bio_RSAPrivateKey!\n");
BIO_free(bp);
RSA_free(rsa);
return 2;
}
printf("open_private_key success to PEM_read_bio_RSAPrivateKey!\n");
m_pPriKey = EVP_PKEY_new();
if(NULL == m_pPriKey)
{
printf("open_private_key EVP_PKEY_new failed\n");
RSA_free(rsa);
return 3;
}
EVP_PKEY_assign_RSA(m_pPriKey, rsa);
return 0;
}
// 使用公钥加密,这种封装格式只适用公钥加密,私钥解密。
int CRsaPeer::Encrypt(
const unsigned char *orig_data,
size_t orig_data_len,
unsigned char *enc_data,
size_t &enc_data_len)
{
if (0 != OpenPublicKey())
{
return -1;
}
EVP_PKEY_CTX *ctx = NULL;
OpenSSL_add_all_ciphers();
ctx = EVP_PKEY_CTX_new(m_pPubKey, NULL);
if(NULL == ctx)
{
printf("Encrypt failed to open ctx.\n");
EVP_PKEY_free(m_pPubKey);
m_pPubKey = NULL;
return -1;
}
if(EVP_PKEY_encrypt_init(ctx) <= 0)
{
printf("ras_pubkey_encryptfailed to EVP_PKEY_encrypt_init.\n");
EVP_PKEY_free(m_pPubKey);
m_pPubKey = NULL;
return -1;
}
if(EVP_PKEY_encrypt(ctx,
enc_data,
&enc_data_len,
orig_data,
orig_data_len) <= 0)
{
printf("ras_pubkey_encryptfailed to EVP_PKEY_encrypt.\n");
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(m_pPubKey);
m_pPubKey = NULL;
return -1;
}
EVP_PKEY_CTX_free(ctx);
//EVP_PKEY_free(m_pPubKey);
return 0;
}
// 使用私钥解密,这种封装格式只适用公钥加密,私钥解密
int CRsaPeer::Decrypt(
const unsigned char *enc_data,
size_t enc_data_len,
unsigned char *orig_data,
size_t &orig_data_len
)
{
if (0 != OpenPrivateKey())
{
return -1;
}
EVP_PKEY_CTX *ctx = NULL;
OpenSSL_add_all_ciphers();
ctx = EVP_PKEY_CTX_new(m_pPriKey, NULL);
if(NULL == ctx)
{
printf("RSAKeyDecrypt failed to open ctx.\n");
EVP_PKEY_free(m_pPriKey);
m_pPriKey = NULL;
return -1;
}
if(EVP_PKEY_decrypt_init(ctx) <= 0)
{
printf("RSAKeyDecrypt failed to EVP_PKEY_decrypt_init.\n");
EVP_PKEY_free(m_pPriKey);
m_pPriKey = NULL;
return -1;
}
if(EVP_PKEY_decrypt(ctx,
orig_data,
&orig_data_len,
enc_data,
enc_data_len) <= 0)
{
printf("RSAKeyDecrypt failed to EVP_PKEY_decrypt.\n");
EVP_PKEY_CTX_free(ctx);
EVP_PKEY_free(m_pPriKey);
m_pPriKey = NULL;
return -1;
}
EVP_PKEY_CTX_free(ctx);
// EVP_PKEY_free(m_pPriKey);
return 0;
}
int CRsaPeer::CreateKeyPairFileInternal()
{
RSA *rsa ;
int modulelen = 1024 ;
int ret ;
unsigned long e = RSA_3 ;
BIGNUM *bn ;
bn = BN_new () ;
ret = BN_set_word ( bn , e ) ;
if ( ret != 1 )
{
perror ("BN_set_word method goes wrong ") ;
return -1 ;
}
rsa = RSA_new () ;
if ( RSA_generate_key_ex ( rsa , modulelen , bn , NULL ) != 1 )
{
perror ("RSA_generate_key_ex method goes wrong") ;
return -1 ;
}
//---------------------------------------------------------------
// public key
BIO *bioPtr = BIO_new ( BIO_s_file () ) ;
//----- open public key store file ------
if ( BIO_write_filename ( bioPtr , (void*)m_strPubKeyPath.c_str ()) <= 0 )
{
perror ("failed to open public key file ") ;
return -1 ;
}
//----- write public key into file -----
if ( PEM_write_bio_RSAPublicKey( bioPtr , rsa ) != 1 )
{
perror ("failed to write RSA public key into file") ;
return -1 ;
}
//----- if we get here , everything goes well -----
printf ("generated RSA public key already written into file %s \n" , m_strPubKeyPath.c_str()) ;
BIO_free_all( bioPtr ) ; // don't forget release and free the allocated space
//-----------------------------------------------------------------------------------------
//----- private key -----
bioPtr = BIO_new_file ( m_strPriKeyPath.c_str() , "w+") ;
if ( bioPtr == NULL )
{
perror ("failed to open file stores RSA private key ") ;
return -1 ;
}
if ( PEM_write_bio_RSAPrivateKey ( bioPtr , rsa ,EVP_des_ede3_ofb() ,
(unsigned char *)m_strPasswordForPrivateKey.c_str() , m_strPasswordForPrivateKey.size() , NULL , NULL ) != 1 )
{
perror ("failed write RSA private key into file") ;
return -1 ;
}
BIO_free_all ( bioPtr ) ; // do not forget this
printf ("genertated RSA private key already written into file %s \n" , m_strPriKeyPath.c_str () ) ;
return 0 ;
}
二 客户端生成密钥和发送密钥
BOOL CClientAffairs::ExchangeKey()
{
if (!m_pNetWorkBase->m_socket)
{
return FALSE;
}
string strPubKeyPath = MakeTempFileName();
string strPriKeyPath = MakeTempFileName();
string strPassword = "Wehavetowork8daysperweekbutonlYpayed1.0$";
// 生成公钥和私钥文件
CRsaPeer* rsaKeyPair = CRsaPeer::CreateRsaFiles(strPubKeyPath,strPriKeyPath,strPassword);
DebugMessageA("客户端生成的公钥 = %s",strPubKeyPath.c_str());
if (!rsaKeyPair)
{
ERROR_MSG("生成RSA密钥失败了。");
return FALSE;
}
// 打开公钥文件,读入内容到缓冲区中。
string strPublicKey;
FileReader fr(strPubKeyPath);
if (fr.open())
{
strPublicKey = fr.read();
fr.close();
}
if (strPublicKey.empty())
{
ERROR_MSG("读取公钥失败了。");
delete rsaKeyPair;
return FALSE;
}
m_rsaPeerClient = rsaKeyPair;
int iSend = m_pNetWorkBase->SendMsg(strPublicKey,NETMSG_EXC_RSA_PUBLIC_KEY);
if (iSend == MEMORY_BAD_ALLOC)
{
ERROR_MSG("申请缓冲区失败了。");
}
if (iSend <= 0)
{
return FALSE;
}
BOOL bRet = FALSE;
m_iAffairId = affair_exchange_public_key;
DWORD dwWait = WaitForSingleObject(m_hEventExcKey,TIMEOUT_OF_EXCHANGE_KEY);
if (dwWait == WAIT_OBJECT_0)
{
bRet = TRUE;
}
//else time out or error happened.
return bRet;
}
三 服务器接收密钥、生成服务器密钥并发送给客户端
BOOL PackNetworkObject::OnExcPubKeys( BYTE *pData, WORD wSize )
{
if (m_rsaKeyClientPub || m_rsaKeyServerPair)
{
DebugMessageA("Error,sesson %d has exchanged pubkey!",GetSession()->GetSocket());
return FALSE;
}
// 保存到临时文件
string strClientPubKeyFile = MakeTempFileName();
FileWriter fw(strClientPubKeyFile);
string data((char*)pData,wSize);
fw.write(data);
fw.close();
DebugMessageA("服务器收到了客户端的公钥并保存在 : %s",strClientPubKeyFile.c_str());
// 仅存放公钥
m_rsaKeyClientPub = new CRsaPeer(strClientPubKeyFile,"","");
// 检查是否能正常打开这个公钥
if (0 != m_rsaKeyClientPub->OpenPublicKey())
{
return FALSE;
}
// 服务器生成一对KEY,将公钥发送给客户端使用。
string strPubKeyPath = MakeTempFileName();
string strPriKeyPath = MakeTempFileName();
string strPassword = "Wehavetowork8daysperweekbutonlYpayed1.0$";
// 生成公钥和私钥文件
m_rsaKeyServerPair = CRsaPeer::CreateRsaFiles(strPubKeyPath,strPriKeyPath,strPassword);
if (!m_rsaKeyServerPair)
{
DebugMessageA("生成RSA密钥失败了。");
return FALSE;
}
// 打开公钥文件,读入内容到缓冲区中。
string strPublicKey;
FileReader fr(strPubKeyPath);
if (fr.open())
{
strPublicKey = fr.read();
fr.close();
}
if (strPublicKey.empty())
{
DebugMessageA("读取公钥失败了。");
return FALSE;
}
// 发送出去
return SendMsg(strPublicKey,NETMSG_EXC_RSA_PUBLIC_KEY);
}
四 客户端接收并保存服务器的公钥
int CClientAffairs::OnExcRsaPublicKeyRecv( char* pRecvBuffer, int packlen )
{
string strSrvKeyFile = MakeTempFileName();
FileWriter fw(strSrvKeyFile);
string data(pRecvBuffer,packlen);
fw.write(data);
fw.close();
// 仅存放公钥
ASSERT(m_rsaPeerServer == NULL);
m_rsaPeerServer = new CRsaPeer(strSrvKeyFile,"","");
// 检查是否能正常打开这个公钥
if (0 == m_rsaPeerServer->OpenPublicKey())
{
SetEvent(m_hEventExcKey);
}
//
return 0;
}
五 客户端使用服务器公钥加密账号密码信息并且发送
BOOL CClientAffairs::Login( const std::string& acc,const std::string& pswd )
{
if (acc.empty() || pswd.empty())
{
ERROR_MSG("长度有问题。");
return FALSE;
}
unsigned char BufferForEncodeAcc[1024] = {0};
unsigned char BufferForEncodePswd[1024] = {0};
if (!m_rsaPeerClient ||
!m_rsaPeerServer)
{
ERROR_MSG("密钥未准备好。");
return FALSE;
}
size_t iEnAccSize = sizeof(BufferForEncodeAcc);
//使用服务器提供的公钥加密账号
if (0 != m_rsaPeerServer->Encrypt((const unsigned char*)acc.c_str(),acc.length(),BufferForEncodeAcc,iEnAccSize))
{
ERROR_MSG("加密失败 - 1。");
return FALSE;
}
if (0 == iEnAccSize)
{
ERROR_MSG("加密失败 - 2。");
return FALSE;
}
//使用服务器提供的公钥加密密码
size_t iEnPswdSize = sizeof(BufferForEncodePswd);
if (0 != m_rsaPeerServer->Encrypt((const unsigned char*)pswd.c_str(),pswd.length(),BufferForEncodePswd,iEnPswdSize))
{
ERROR_MSG("加密失败 - 3。");
return FALSE;
}
if (0 == iEnPswdSize)
{
ERROR_MSG("加密失败 - 4。");
return FALSE;
}
//组织缓冲区并发送
//缓冲区的格式(账号长度(USHORT) + 密码长度(USHORT) + 账号 + 密码)
size_t needlen = iEnAccSize + sizeof(WORD)*2 + iEnPswdSize;
char* pBuf = new char[needlen];
BufferSetV<WORD>(pBuf, 0, LOWORD(iEnAccSize));
BufferSetV<WORD>(pBuf, 2, LOWORD(iEnPswdSize));
memcpy(pBuf + 4, BufferForEncodeAcc, iEnAccSize);
memcpy(pBuf + 4 + iEnAccSize , BufferForEncodePswd, iEnPswdSize);
BOOL bRet = (0 < m_pNetWorkBase->SendMsg(pBuf,needlen,NETMSG_EXC_RSA_LOGIN));
delete [] pBuf;
if (!bRet)
{
return FALSE;
}
//等待服务器的相应,等待的时间为TIMEOUT_OF_LOGIN
m_iAffairId = affair_login;
DWORD dwWait = WaitForSingleObject(m_hEventLogin,TIMEOUT_OF_LOGIN);
if (dwWait == WAIT_OBJECT_0)
{
bRet = TRUE;
}
else
{
ERROR_MSG("等待服务器登录响应失败了");
return FALSE;
}
//
return bRet;
}
六 服务器验证账号密码信息并发送用户信息
BOOL PackNetworkObject::OnLogin( BYTE *pData, WORD wSize )
{
ASSERT(wSize >= 4);
if (!m_rsaKeyClientPub || !m_rsaKeyServerPair)
{
DebugMessageA("Error,sesson %d has NOT exchanged pubkey!",GetSession()->GetSocket());
return FALSE;
}
// 1 从数据中分割出账号,密码。头两个字节分别为加密后账号密码的长度
WORD accLen = BufferGetV<WORD>(pData,0);
WORD pswdLen = BufferGetV<WORD>(pData,2);
// 1.1 校验长度
if (accLen + pswdLen + 4 != wSize)
{
DebugMessageA("Error,sesson %d 登录数据包长度不对!",GetSession()->GetSocket());
return FALSE;
}
// 1.2 分离账号
BYTE* pEncAcc = new BYTE[accLen];
if (!pEncAcc)
{
return FALSE;
}
BYTE* pEncPsw = new BYTE[pswdLen];
if (!pEncPsw)
{
return FALSE;
}
memcpy(pEncAcc, pData+4, accLen);
memcpy(pEncPsw, pData+4+accLen, pswdLen);
BOOL bDecodeResult = FALSE;
BOOL bLoginResult = FALSE;
BYTE pDecAcc[1024] = {0};
BYTE pDecPsw[1024] = {0};
do
{
// 2 使用服务器私钥解密
size_t DecAccLen = sizeof(pDecAcc);
memset(pDecAcc,0,sizeof(pDecAcc));
size_t DecPswLen = sizeof(pDecPsw);
memset(pDecPsw,0,sizeof(pDecPsw));
if (0 != m_rsaKeyServerPair->Decrypt(pEncAcc,accLen,pDecAcc,DecAccLen))
{
DebugMessageA("Error,sesson %d 解密账号失败 - 1!",GetSession()->GetSocket());
break;
}
if (DecAccLen <= 0)
{
DebugMessageA("Error,sesson %d 解密账号失败 - 2!",GetSession()->GetSocket());
break;
}
if (0 != m_rsaKeyServerPair->Decrypt(pEncPsw,pswdLen,pDecPsw,DecPswLen))
{
DebugMessageA("Error,sesson %d 解密密码失败 - 1!",GetSession()->GetSocket());
break;
}
if (DecPswLen <= 0)
{
DebugMessageA("Error,sesson %d 解密密码失败 - 2!",GetSession()->GetSocket());
break;
}
bDecodeResult = TRUE;
}while(false);
delete [] pEncAcc;
delete [] pEncPsw;
if (!bDecodeResult)
{
Disconnect();
}
int iResultVerify = VerifyUserAuthrizetion(pDecAcc,pDecPsw);
if (iResultVerify != 0)
{
m_bLogined = FALSE;
DebugMessageA("Error,sesson %d 校验账号密码失败,id=%d!",GetSession()->GetSocket(),iResultVerify);
BYTE LoginFailedMsg[4] = {0};
BufferSetV<int>(LoginFailedMsg,0,iResultVerify);
return SendMsg(LoginFailedMsg,4,NETMSG_ACK_LOGIN_FAILED);
}
else
{
ASSERT(m_pUserInfo);
if (!m_pUserInfo)
{
Disconnect();
return FALSE;
}
string strUserInfo = m_pUserInfo->toJsonString();
if (strUserInfo.empty())
{
Disconnect();
return FALSE;
}
DebugMessageA("sesson %d 成功登录,用户名: %s!",GetSession()->GetSocket(),m_pUserInfo->m_strUserName.c_str());
m_bLogined = TRUE;
BYTE pEncUserInfo[1024] = {0};
size_t SizeOfEncBuf = sizeof(pEncUserInfo);
if (0 != m_rsaKeyClientPub->Encrypt((BYTE*)strUserInfo.c_str(),strUserInfo.length(),pEncUserInfo,SizeOfEncBuf)
|| 0 == SizeOfEncBuf)
{
DebugMessageA("加密失败了");
Disconnect();
return FALSE;
}
int msgLen = SizeOfEncBuf;
BYTE* LoginMsg = new BYTE[msgLen];
if (!LoginMsg)
{
Disconnect();
return FALSE;
}
// TODO 组织缓冲区要改成用流式处理,经常漏算偏移。
memcpy(LoginMsg, pEncUserInfo, SizeOfEncBuf);
BOOL bRet = SendMsg(LoginMsg,msgLen,NETMSG_ACK_LOGIN_SUCCESS);
delete [] LoginMsg;
return bRet;
}
}
七 客户端解密用户信息
int CClientAffairs::OnLoginSuccess( char* pRecvBuffer,int packlen )
{
char Desc[1024] = {0};
size_t descSize = sizeof(Desc);
int iRet = m_rsaPeerClient->Decrypt((const unsigned char*)pRecvBuffer,packlen,(unsigned char*)Desc,descSize);
if (iRet != 0 || descSize >= sizeof(Desc))
{
ERROR_MSG("解密失败,返回%d",iRet);
return -1;
}
Desc[descSize] = 0;
ERROR_MSG("成功获取用户信息:%s",Desc);
m_UserInfo = CUserInfo::fromJsonString(Desc);
return 0;
}