OpenSLL服务器和客户端交换RSA公钥

来源:互联网 发布:html数据展示样式 编辑:程序博客网 时间:2024/06/15 18:08

这个代码用于登录服务器。

第一步:客户端生成一对公钥和私钥,并将客户端公钥发给服务器。

第二步:服务器生成一对公钥和密钥,并将服务器公钥发给客户端。

第三步:客户端使用服务器公钥给登录账号密码加密发给服务器,服务器使用服务器私钥解密,并校验账号密码的正确性。

第四步:如果密码正确,服务器读取数据库中的用户信息,使用客户端的公钥加密,发给客户端

第五步:客户端使用客户端私钥将用户信息解密。


一 生成密钥和加解密算法的封装

头文件

#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 keyBIO *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_LOGINm_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;}


0 0
原创粉丝点击