银行安全传输平台(五)OpenSSL设置和RSA模块

打印 上一主题 下一主题

主题 1544|帖子 1544|积分 4632

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x

前言

为了实现密钥的互换,我们必要将协商好的对称加密密钥利用RSA加密传输,这里我们就要用到OpenSSL库,因为博主本身在校期间已经花了很大一部分时间在学密码,以是本篇条记就不再详细报告RSA或者哈希函数背后的数论知识,只简单介绍接口,封装好我们所要用的模块即可。

一、OpenSSL

OpenSSL 是一个安全套接字层密码库,席卷重要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用步伐供测试或其它目的利用。
1.1 设置OpenSSL

因为这个项目重要实在Linux上运行,以是我重要记载Linux下怎样设置,Windows稍微麻烦一点,感兴趣的可以自己相识一下。
安装包下载地址:https://www.openssl.org/source/
这里我以openssl-3.0.13为例
首先解压缩:
  1. $ tar zxvf openssl-3.0.13.tar.gzip
  2. # 加压完成得到目录: openssl-3.0.13
复制代码
然后进入解压目录openssl-3.0.13,安装:
  1. $ ./config
  2. $ make
  3. $ make test                
  4. $ make install         (使用管理员权限)
复制代码
然后验证是否安装乐成:
  1. openssl version -a
复制代码
如果安装乐成应该会输出版本号
二、密码学基础

这里我们简单记载一下密码学的一些基础概念,以理解这些东西在整个加密通信过程中做了些什么。
加密三要素、对称加密非对称加密这些概念不再介绍,重要理解对称加密效率高非对称加密效率低和几种常用的加密方式,我们在这个项目中,用rsa来进行密钥互换,用aes进行加密通信即可。
接下来我们重要介绍秘钥互换、数字前面、消息认证码、哈希算法等知识。
2.1 秘钥互换

假设我们有两个要通信的两边客户端c和服务端s
非对称加密秘钥分发方便, 但是效率低 -> 改进: 必要利用对称加密;
利用对称加密 -> 秘钥分发困难 -> 改进: 利用非对称加密进行秘钥分发。


  • 分发是对称加密的秘钥, 本质就是一个字符串

    • 在服务器端天生一个非对称加密的密钥对: 公钥, 私钥
    • 服务器将公钥发送给客户端, 客户端有了公钥
    • 在客户端天生一个随机字符串 -> 这就是对称加密必要利用的秘钥
    • 在客户端利用公钥对天生的对称加密的秘钥进行加密 -> 密文
    • 将加密的密文发送给服务器
    • 服务器端利用私钥解密 -> 对称加密的秘钥
    • 两边利用同一秘钥进行对称加密通信

2.2 哈希算法

简单说,不管原始数据多长,经过哈希算法计算,得到固定长度的数据,一个安全的哈希算法应该是抗碰撞的(很难找到两个数据对应到同一个数据),以是可以用来将进行勾走消息认证码HMAC。
一定要注意的是哈希算法不是加密算法。
2.3 消息认证码

重要作用是用来查验数据是否被篡改
(原始数据 + 秘钥) * 哈希函数 = 消息认证码
发送方将原始数据和消息认证码加密之后发送出去,接收方解密之后用同样的算法对原始数据进行哈希看看是否匹配,匹配则阐明没有被篡改,否则阐明被篡改了。
2.4 数字署名

用私钥署名公钥验证,一个安全的署名算法应该还要加上消息认证码,重要作用是验证发送者的身份,证明发送者是发送者。
三、封装密码学接口

关于openssl的api这里不再介绍,我们直接来看怎样封装我们所要用到的接口。
首先照旧要阐明的是这里面有一个base64编码的用法我们留到下一篇条记表明
3.1 RsaCrypto.h

  1. #pragma once
  2. #include <string>
  3. #include <openssl/rsa.h>
  4. #include <openssl/pem.h>
  5. using namespace std;
  6. enum SignLevel
  7. {
  8.         Level1 = NID_md5,
  9.         Level2 = NID_sha1,
  10.         Level3 = NID_sha224,
  11.         Level4 = NID_sha256,
  12.         Level5 = NID_sha384,
  13.         Level6 = NID_sha512
  14. };
  15. class RsaCrypto
  16. {
  17. public:
  18.         RsaCrypto();
  19.         RsaCrypto(string fileName, bool isPrivate = true);
  20.         ~RsaCrypto();
  21.         // 通过解析字符串得到秘钥
  22.         void parseKeyString(string keystr, bool pubKey = true);
  23.         // 生成RSA密钥对
  24.         void generateRsakey(int bits, string pub = "public.pem", string pri = "private.pem");
  25.         // 公钥加密
  26.         string rsaPubKeyEncrypt(string data);
  27.         // 私钥解密
  28.         string rsaPriKeyDecrypt(string encData);
  29.         // 使用RSA签名
  30.         string rsaSign(string data, SignLevel level = Level3);
  31.         // 使用RSA验证签名
  32.         bool rsaVerify(string data, string signData, SignLevel level = Level3);
  33.         // base64编码
  34. private:
  35.         string toBase64(const char* str, int len);
  36.         // base64解码
  37.         char* fromBase64(string str);
  38.         // 得到公钥
  39.         bool initPublicKey(string pubfile);
  40.         // 得到私钥
  41.         bool initPrivateKey(string prifile);
  42. private:
  43.         RSA* m_publicKey;        // 私钥
  44.         RSA* m_privateKey;        // 公钥
  45. };
复制代码
由于c++中不建议利用宏,以是我们利用常量/枚举/内联->空间换时间,这里我们默认用sha224来进行署名和认证。
3.2 RsaCrypto.cpp

  1. #include "RsaCrypto.h"
  2. #include <openssl/bio.h>
  3. #include <openssl/err.h>
  4. #include <iostream>
  5. #include <openssl/buffer.h>
  6. #include <string.h>
  7. RsaCrypto::RsaCrypto()
  8. {
  9.         m_publicKey = RSA_new();
  10.         m_privateKey = RSA_new();
  11. }
  12. RsaCrypto::RsaCrypto(string fileName, bool isPrivate)
  13. {
  14.         m_publicKey = RSA_new();
  15.         m_privateKey = RSA_new();
  16.         if (isPrivate)
  17.         {
  18.                 initPrivateKey(fileName);
  19.         }
  20.         else
  21.         {
  22.                 initPublicKey(fileName);
  23.         }
  24. }
  25. RsaCrypto::~RsaCrypto()
  26. {
  27.         RSA_free(m_publicKey);
  28.         RSA_free(m_privateKey);
  29. }
  30. // 将公钥/私钥字符串数据解析到 RSA 对象中
  31. void RsaCrypto::parseKeyString(string keystr, bool pubKey)
  32. {
  33.         // 字符串数据 -> BIO对象中
  34.         BIO* bio = BIO_new_mem_buf(keystr.data(), keystr.size());
  35.         // 公钥字符串
  36.         if (pubKey)
  37.         {
  38.                 PEM_read_bio_RSAPublicKey(bio, &m_publicKey, NULL, NULL);
  39.         }
  40.         else
  41.         {
  42.                 // 私钥字符串
  43.                 PEM_read_bio_RSAPrivateKey(bio, &m_privateKey, NULL, NULL);
  44.         }
  45.         BIO_free(bio);
  46. }
  47. void RsaCrypto::generateRsakey(int bits, string pub, string pri)
  48. {
  49.         RSA* r = RSA_new();
  50.         // 生成RSA密钥对
  51.         // 创建bignum对象
  52.         BIGNUM* e = BN_new();
  53.         // 初始化bignum对象
  54.         BN_set_word(e, 456787);
  55.         RSA_generate_key_ex(r, bits, e, NULL);
  56.         // 创建bio文件对象
  57.         BIO* pubIO = BIO_new_file(pub.data(), "w");
  58.         // 公钥以pem格式写入到文件中
  59.         PEM_write_bio_RSAPublicKey(pubIO, r);
  60.         // 缓存中的数据刷到文件中
  61.         BIO_flush(pubIO);
  62.         BIO_free(pubIO);
  63.         // 创建bio对象
  64.         BIO* priBio = BIO_new_file(pri.data(), "w");
  65.         // 私钥以pem格式写入文件中
  66.         PEM_write_bio_RSAPrivateKey(priBio, r, NULL, NULL, 0, NULL, NULL);
  67.         BIO_flush(priBio);
  68.         BIO_free(priBio);
  69.         // 得到公钥和私钥
  70.         m_privateKey = RSAPrivateKey_dup(r);
  71.         m_publicKey = RSAPublicKey_dup(r);
  72.         // 释放资源
  73.         BN_free(e);
  74.         RSA_free(r);
  75. }
  76. bool RsaCrypto::initPublicKey(string pubfile)
  77. {
  78.         // 通过BIO读文件
  79.         BIO* pubBio = BIO_new_file(pubfile.data(), "r");
  80.         // 将bio中的pem数据读出
  81.         if (PEM_read_bio_RSAPublicKey(pubBio, &m_publicKey, NULL, NULL) == NULL)
  82.         {
  83.                 ERR_print_errors_fp(stdout);
  84.                 return false;
  85.         }
  86.         BIO_free(pubBio);
  87.         return true;
  88. }
  89. bool RsaCrypto::initPrivateKey(string prifile)
  90. {
  91.         // 通过bio读文件
  92.         BIO* priBio = BIO_new_file(prifile.data(), "r");
  93.         // 将bio中的pem数据读出
  94.         if (PEM_read_bio_RSAPrivateKey(priBio, &m_privateKey, NULL, NULL) == NULL)
  95.         {
  96.                 ERR_print_errors_fp(stdout);
  97.                 return false;
  98.         }
  99.         BIO_free(priBio);
  100.         return true;
  101. }
  102. string RsaCrypto::rsaPubKeyEncrypt(string data)
  103. {
  104.         cout << "加密数据长度: " << data.size() << endl;
  105.         // 计算公钥长度
  106.         int keyLen = RSA_size(m_publicKey);
  107.         cout << "pubKey len: " << keyLen << endl;
  108.         // 申请内存空间
  109.         char* encode = new char[keyLen + 1];
  110.         // 使用公钥加密
  111.         int ret = RSA_public_encrypt(data.size(), (const unsigned char*)data.data(),
  112.                 (unsigned char*)encode, m_publicKey, RSA_PKCS1_PADDING);
  113.         string retStr = string();
  114.         if (ret >= 0)
  115.         {
  116.                 // 加密成功
  117.                 cout << "ret: " << ret << ", keyLen: " << keyLen << endl;
  118.                 retStr = toBase64(encode, ret);
  119.         }
  120.         else
  121.         {
  122.                 ERR_print_errors_fp(stdout);
  123.         }
  124.         // 释放资源
  125.         delete[]encode;
  126.         return retStr;
  127. }
  128. string RsaCrypto::rsaPriKeyDecrypt(string encData)
  129. {
  130.         // text指向的内存需要释放
  131.         char* text = fromBase64(encData);
  132.         // 计算私钥长度
  133.         //cout << "解密数据长度: " << text.size() << endl;
  134.         int keyLen = RSA_size(m_privateKey);
  135.         // 使用私钥解密
  136.         char* decode = new char[keyLen + 1];
  137.         // 数据加密完成之后, 密文长度 == 秘钥长度
  138.         int ret = RSA_private_decrypt(keyLen, (const unsigned char*)text,
  139.                 (unsigned char*)decode, m_privateKey, RSA_PKCS1_PADDING);
  140.         string retStr = string();
  141.         if (ret >= 0)
  142.         {
  143.                 retStr = string(decode, ret);
  144.         }
  145.         else
  146.         {
  147.                 cout << "私钥解密失败..." << endl;
  148.                 ERR_print_errors_fp(stdout);
  149.         }
  150.         delete[]decode;
  151.         delete[]text;
  152.         return retStr;
  153. }
  154. string RsaCrypto::rsaSign(string data, SignLevel level)
  155. {
  156.         unsigned int len;
  157.         char* signBuf = new char[1024];
  158.         memset(signBuf, 0, 1024);
  159.         int ret = RSA_sign(level, (const unsigned char*)data.data(), data.size(), (unsigned char*)signBuf,
  160.                 &len, m_privateKey);
  161.         if (ret == -1)
  162.         {
  163.                 ERR_print_errors_fp(stdout);
  164.         }
  165.         cout << "sign len: " << len << ", ret: " << ret << endl;
  166.         string retStr = toBase64(signBuf, len);
  167.         delete[]signBuf;
  168.         return retStr;
  169. }
  170. bool RsaCrypto::rsaVerify(string data, string signData, SignLevel level)
  171. {
  172.         // 验证签名
  173.         int keyLen = RSA_size(m_publicKey);
  174.         char* sign = fromBase64(signData);
  175.         int ret = RSA_verify(level, (const unsigned char*)data.data(), data.size(),
  176.                 (const unsigned char*)sign, keyLen, m_publicKey);
  177.         delete[]sign;
  178.         if (ret == -1)
  179.         {
  180.                 ERR_print_errors_fp(stdout);
  181.         }
  182.         if (ret != 1)
  183.         {
  184.                 return false;
  185.         }
  186.         return true;
  187. }
  188. string RsaCrypto::toBase64(const char* str, int len)
  189. {
  190.         BIO* mem = BIO_new(BIO_s_mem());
  191.         BIO* bs64 = BIO_new(BIO_f_base64());
  192.         // mem添加到bs64中
  193.         bs64 = BIO_push(bs64, mem);
  194.         // 写数据
  195.         BIO_write(bs64, str, len);
  196.         BIO_flush(bs64);
  197.         // 得到内存对象指针
  198.         BUF_MEM *memPtr;
  199.         BIO_get_mem_ptr(bs64, &memPtr);
  200.         string retStr = string(memPtr->data, memPtr->length - 1);
  201.         BIO_free_all(bs64);
  202.         return retStr;
  203. }
  204. char* RsaCrypto::fromBase64(string str)
  205. {
  206.         int length = str.size();
  207.         BIO* bs64 = BIO_new(BIO_f_base64());
  208.         BIO* mem = BIO_new_mem_buf(str.data(), length);
  209.         BIO_push(bs64, mem);
  210.         char* buffer = new char[length];
  211.         memset(buffer, 0, length);
  212.         BIO_read(bs64, buffer, length);
  213.         BIO_free_all(bs64);
  214.         return buffer;
  215. }
复制代码
要想表明清晰这个模块,照旧要介绍一下里面一些比较陌生的api,如下:
  1. #include <openssl/rsa.h>
  2. // 申请一块内存, 存储了公钥和私钥
  3. // 如果想得到RSA类型变量必须使用 RSA_new();
  4. RSA *RSA_new(void);
  5. void RSA_free(RSA *);
  6. BIGNUM* BN_new(void);
  7. void BN_free(BIGNUM*);
  8. // 生成密钥对, 密钥对存储在内存中
  9. int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
  10.         参数:
  11.                 - rsa: 通过RSA_new()获得
  12.                 - bits: 秘钥长度, 单位: bit, 常用的长度 1024*n (n正整数)
  13.         - e: 比较大的数(5位以内)
  14.             - 通过 BN_new 得到对应的变量
  15.             - 初始化: BN_set_word(e, 12345);
  16.                 - cb: 回调函数, 用不到, 直接写NULL
  17. // rsa公钥私钥类型是一样的: RSA类型
  18. // 将参数rsa中的公钥提取出来
  19. RSA *RSAPublicKey_dup(RSA *rsa);
  20.         - rsa参数: 秘钥信息
  21.         - 返回值: rsa公钥
  22. // 将参数rsa中的私钥提取出来
  23. RSA *RSAPrivateKey_dup(RSA *rsa);
  24.         - rsa参数: 秘钥信息
  25.         - 返回值: rsa私钥
  26. // 创建bio对象
  27. // 密钥对写磁盘文件的时候, 需要编码 -> base64
  28. // 封装了fopen
  29. BIO *BIO_new_file(const char *filename, const char *mode);
  30.         参数:
  31.                 - filename: 文件名
  32.                 - mode: 文件打开方式和fopen打开方式的指定相同
  33.                
  34. int PEM_write_bio_RSAPublicKey(BIO* bp, const RSA* r);
  35. int PEM_write_bio_RSAPrivateKey(BIO* bp, const RSA* r, const EVP_CIPHER* enc,
  36.         unsigned char* kstr, int klen, pem_password_cb *cb, void* u);
  37. RSA* PEM_read_bio_RSAPublicKey(BIO* bp, RSA** r, pem_password_cb *cb, void* u);
  38. RSA* PEM_read_bio_RSAPrivateKey(BIO* bp, RSA** r, pem_password_cb *cb, void* u);
  39.         参数:
  40.                 - bp: 通过BIO_new_file();函数得到该对象
  41.                 - r: 传递一个RSA* rsa指针的地址, 传出参数-> 公钥/私钥
  42.                 - cb: 回调函数, 用不到, 指定为NULL
  43.                 - u: 给回调传参, 用不到, 指定为NULL
  44. //
  45. //
  46. RSA* PEM_read_RSAPublicKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
  47. RSA* PEM_read_RSAPrivateKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
  48. // 写入文件中的公钥私钥数据不是原始数据, 写入的编码之后的数据
  49. // 是一种pem的文件格式, 数据使用base64进行编码
  50. int PEM_write_RSAPublicKey(FILE* fp, const RSA* r);
  51. int PEM_write_RSAPrivateKey(FILE* fp, const RSA* r, const EVP_CIPHER* enc,
  52.         unsigned char* kstr, int klen, pem_password_cb *cb, void* u);       
  53.         参数:
  54.                 - fp: 需要打开一个磁盘文件, 并且指定写权限
  55.                 - r: 存储了密钥对
  56.                  - 私钥独有的参数
  57.                 - enc: 指定的加密算法 -> 对称加密 -> NULL
  58.                 - kstr: 对称加密的秘钥 -> NULL
  59.                 - klen: 秘钥长度 -> 0
  60.                 - cb: 回调函数, 用不到, NULL
  61.                 - u: 给回调传参, 用不到, NULL
复制代码
以是整个代码块看下来重要完成了这几个任务:天生密钥对、公钥加密、私钥解密、数据署名、验证署名。至于toBase64和fromBase64就是为相识决我们的加密后的密钥在传输过程中解决的一些问题,这都是后话了,以是团体来看,这段代码可以抽象至如下表示:
  1. class MyRSA
  2. {
  3. public:
  4.     MyRSA();
  5.     ~MyRSA;
  6.     // 生成密钥对
  7.     // 公钥加密
  8.     // 私钥解密
  9.     // 数据签名
  10.     // 验证签名
  11. private:
  12.     RSA* pubkey;
  13.     RSA* pirKey;
  14. }
复制代码

四、关于hsah模块的阐明

我们在这个项目中其实还封装了一个hash模块来调用,不过那是到最后封装外联接口支持网点之间直接用aes加解密的情况了,以是我们也是放到之后在做记载。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

张裕

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表