马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
前言
为了实现密钥的互换,我们必要将协商好的对称加密密钥利用RSA加密传输,这里我们就要用到OpenSSL库,因为博主本身在校期间已经花了很大一部分时间在学密码,以是本篇条记就不再详细报告RSA或者哈希函数背后的数论知识,只简单介绍接口,封装好我们所要用的模块即可。
一、OpenSSL
OpenSSL 是一个安全套接字层密码库,席卷重要的密码算法、常用的密钥和证书封装管理功能及SSL协议,并提供丰富的应用步伐供测试或其它目的利用。
1.1 设置OpenSSL
因为这个项目重要实在Linux上运行,以是我重要记载Linux下怎样设置,Windows稍微麻烦一点,感兴趣的可以自己相识一下。
安装包下载地址:https://www.openssl.org/source/
这里我以openssl-3.0.13为例
首先解压缩:
- $ tar zxvf openssl-3.0.13.tar.gzip
- # 加压完成得到目录: openssl-3.0.13
复制代码 然后进入解压目录openssl-3.0.13,安装:
- $ ./config
- $ make
- $ make test
- $ make install (使用管理员权限)
复制代码 然后验证是否安装乐成:
如果安装乐成应该会输出版本号
二、密码学基础
这里我们简单记载一下密码学的一些基础概念,以理解这些东西在整个加密通信过程中做了些什么。
加密三要素、对称加密非对称加密这些概念不再介绍,重要理解对称加密效率高非对称加密效率低和几种常用的加密方式,我们在这个项目中,用rsa来进行密钥互换,用aes进行加密通信即可。
接下来我们重要介绍秘钥互换、数字前面、消息认证码、哈希算法等知识。
2.1 秘钥互换
假设我们有两个要通信的两边客户端c和服务端s
非对称加密秘钥分发方便, 但是效率低 -> 改进: 必要利用对称加密;
利用对称加密 -> 秘钥分发困难 -> 改进: 利用非对称加密进行秘钥分发。
- 分发是对称加密的秘钥, 本质就是一个字符串
- 在服务器端天生一个非对称加密的密钥对: 公钥, 私钥
- 服务器将公钥发送给客户端, 客户端有了公钥
- 在客户端天生一个随机字符串 -> 这就是对称加密必要利用的秘钥
- 在客户端利用公钥对天生的对称加密的秘钥进行加密 -> 密文
- 将加密的密文发送给服务器
- 服务器端利用私钥解密 -> 对称加密的秘钥
- 两边利用同一秘钥进行对称加密通信
2.2 哈希算法
简单说,不管原始数据多长,经过哈希算法计算,得到固定长度的数据,一个安全的哈希算法应该是抗碰撞的(很难找到两个数据对应到同一个数据),以是可以用来将进行勾走消息认证码HMAC。
一定要注意的是哈希算法不是加密算法。
2.3 消息认证码
重要作用是用来查验数据是否被篡改
(原始数据 + 秘钥) * 哈希函数 = 消息认证码
发送方将原始数据和消息认证码加密之后发送出去,接收方解密之后用同样的算法对原始数据进行哈希看看是否匹配,匹配则阐明没有被篡改,否则阐明被篡改了。
2.4 数字署名
用私钥署名公钥验证,一个安全的署名算法应该还要加上消息认证码,重要作用是验证发送者的身份,证明发送者是发送者。
三、封装密码学接口
关于openssl的api这里不再介绍,我们直接来看怎样封装我们所要用到的接口。
首先照旧要阐明的是这里面有一个base64编码的用法我们留到下一篇条记表明
3.1 RsaCrypto.h
- #pragma once
- #include <string>
- #include <openssl/rsa.h>
- #include <openssl/pem.h>
- using namespace std;
- enum SignLevel
- {
- Level1 = NID_md5,
- Level2 = NID_sha1,
- Level3 = NID_sha224,
- Level4 = NID_sha256,
- Level5 = NID_sha384,
- Level6 = NID_sha512
- };
- class RsaCrypto
- {
- public:
- RsaCrypto();
- RsaCrypto(string fileName, bool isPrivate = true);
- ~RsaCrypto();
- // 通过解析字符串得到秘钥
- void parseKeyString(string keystr, bool pubKey = true);
- // 生成RSA密钥对
- void generateRsakey(int bits, string pub = "public.pem", string pri = "private.pem");
- // 公钥加密
- string rsaPubKeyEncrypt(string data);
- // 私钥解密
- string rsaPriKeyDecrypt(string encData);
- // 使用RSA签名
- string rsaSign(string data, SignLevel level = Level3);
- // 使用RSA验证签名
- bool rsaVerify(string data, string signData, SignLevel level = Level3);
- // base64编码
- private:
- string toBase64(const char* str, int len);
- // base64解码
- char* fromBase64(string str);
- // 得到公钥
- bool initPublicKey(string pubfile);
- // 得到私钥
- bool initPrivateKey(string prifile);
- private:
- RSA* m_publicKey; // 私钥
- RSA* m_privateKey; // 公钥
- };
复制代码 由于c++中不建议利用宏,以是我们利用常量/枚举/内联->空间换时间,这里我们默认用sha224来进行署名和认证。
3.2 RsaCrypto.cpp
- #include "RsaCrypto.h"
- #include <openssl/bio.h>
- #include <openssl/err.h>
- #include <iostream>
- #include <openssl/buffer.h>
- #include <string.h>
- RsaCrypto::RsaCrypto()
- {
- m_publicKey = RSA_new();
- m_privateKey = RSA_new();
- }
- RsaCrypto::RsaCrypto(string fileName, bool isPrivate)
- {
- m_publicKey = RSA_new();
- m_privateKey = RSA_new();
- if (isPrivate)
- {
- initPrivateKey(fileName);
- }
- else
- {
- initPublicKey(fileName);
- }
- }
- RsaCrypto::~RsaCrypto()
- {
- RSA_free(m_publicKey);
- RSA_free(m_privateKey);
- }
- // 将公钥/私钥字符串数据解析到 RSA 对象中
- void RsaCrypto::parseKeyString(string keystr, bool pubKey)
- {
- // 字符串数据 -> BIO对象中
- BIO* bio = BIO_new_mem_buf(keystr.data(), keystr.size());
- // 公钥字符串
- if (pubKey)
- {
- PEM_read_bio_RSAPublicKey(bio, &m_publicKey, NULL, NULL);
- }
- else
- {
- // 私钥字符串
- PEM_read_bio_RSAPrivateKey(bio, &m_privateKey, NULL, NULL);
- }
- BIO_free(bio);
- }
- void RsaCrypto::generateRsakey(int bits, string pub, string pri)
- {
- RSA* r = RSA_new();
- // 生成RSA密钥对
- // 创建bignum对象
- BIGNUM* e = BN_new();
- // 初始化bignum对象
- BN_set_word(e, 456787);
- RSA_generate_key_ex(r, bits, e, NULL);
- // 创建bio文件对象
- BIO* pubIO = BIO_new_file(pub.data(), "w");
- // 公钥以pem格式写入到文件中
- PEM_write_bio_RSAPublicKey(pubIO, r);
- // 缓存中的数据刷到文件中
- BIO_flush(pubIO);
- BIO_free(pubIO);
- // 创建bio对象
- BIO* priBio = BIO_new_file(pri.data(), "w");
- // 私钥以pem格式写入文件中
- PEM_write_bio_RSAPrivateKey(priBio, r, NULL, NULL, 0, NULL, NULL);
- BIO_flush(priBio);
- BIO_free(priBio);
- // 得到公钥和私钥
- m_privateKey = RSAPrivateKey_dup(r);
- m_publicKey = RSAPublicKey_dup(r);
- // 释放资源
- BN_free(e);
- RSA_free(r);
- }
- bool RsaCrypto::initPublicKey(string pubfile)
- {
- // 通过BIO读文件
- BIO* pubBio = BIO_new_file(pubfile.data(), "r");
- // 将bio中的pem数据读出
- if (PEM_read_bio_RSAPublicKey(pubBio, &m_publicKey, NULL, NULL) == NULL)
- {
- ERR_print_errors_fp(stdout);
- return false;
- }
- BIO_free(pubBio);
- return true;
- }
- bool RsaCrypto::initPrivateKey(string prifile)
- {
- // 通过bio读文件
- BIO* priBio = BIO_new_file(prifile.data(), "r");
- // 将bio中的pem数据读出
- if (PEM_read_bio_RSAPrivateKey(priBio, &m_privateKey, NULL, NULL) == NULL)
- {
- ERR_print_errors_fp(stdout);
- return false;
- }
- BIO_free(priBio);
- return true;
- }
- string RsaCrypto::rsaPubKeyEncrypt(string data)
- {
- cout << "加密数据长度: " << data.size() << endl;
- // 计算公钥长度
- int keyLen = RSA_size(m_publicKey);
- cout << "pubKey len: " << keyLen << endl;
- // 申请内存空间
- char* encode = new char[keyLen + 1];
- // 使用公钥加密
- int ret = RSA_public_encrypt(data.size(), (const unsigned char*)data.data(),
- (unsigned char*)encode, m_publicKey, RSA_PKCS1_PADDING);
- string retStr = string();
- if (ret >= 0)
- {
- // 加密成功
- cout << "ret: " << ret << ", keyLen: " << keyLen << endl;
- retStr = toBase64(encode, ret);
- }
- else
- {
- ERR_print_errors_fp(stdout);
- }
- // 释放资源
- delete[]encode;
- return retStr;
- }
- string RsaCrypto::rsaPriKeyDecrypt(string encData)
- {
- // text指向的内存需要释放
- char* text = fromBase64(encData);
- // 计算私钥长度
- //cout << "解密数据长度: " << text.size() << endl;
- int keyLen = RSA_size(m_privateKey);
- // 使用私钥解密
- char* decode = new char[keyLen + 1];
- // 数据加密完成之后, 密文长度 == 秘钥长度
- int ret = RSA_private_decrypt(keyLen, (const unsigned char*)text,
- (unsigned char*)decode, m_privateKey, RSA_PKCS1_PADDING);
- string retStr = string();
- if (ret >= 0)
- {
- retStr = string(decode, ret);
- }
- else
- {
- cout << "私钥解密失败..." << endl;
- ERR_print_errors_fp(stdout);
- }
- delete[]decode;
- delete[]text;
- return retStr;
- }
- string RsaCrypto::rsaSign(string data, SignLevel level)
- {
- unsigned int len;
- char* signBuf = new char[1024];
- memset(signBuf, 0, 1024);
- int ret = RSA_sign(level, (const unsigned char*)data.data(), data.size(), (unsigned char*)signBuf,
- &len, m_privateKey);
- if (ret == -1)
- {
- ERR_print_errors_fp(stdout);
- }
- cout << "sign len: " << len << ", ret: " << ret << endl;
- string retStr = toBase64(signBuf, len);
- delete[]signBuf;
- return retStr;
- }
- bool RsaCrypto::rsaVerify(string data, string signData, SignLevel level)
- {
- // 验证签名
- int keyLen = RSA_size(m_publicKey);
- char* sign = fromBase64(signData);
- int ret = RSA_verify(level, (const unsigned char*)data.data(), data.size(),
- (const unsigned char*)sign, keyLen, m_publicKey);
- delete[]sign;
- if (ret == -1)
- {
- ERR_print_errors_fp(stdout);
- }
- if (ret != 1)
- {
- return false;
- }
- return true;
- }
- string RsaCrypto::toBase64(const char* str, int len)
- {
- BIO* mem = BIO_new(BIO_s_mem());
- BIO* bs64 = BIO_new(BIO_f_base64());
- // mem添加到bs64中
- bs64 = BIO_push(bs64, mem);
- // 写数据
- BIO_write(bs64, str, len);
- BIO_flush(bs64);
- // 得到内存对象指针
- BUF_MEM *memPtr;
- BIO_get_mem_ptr(bs64, &memPtr);
- string retStr = string(memPtr->data, memPtr->length - 1);
- BIO_free_all(bs64);
- return retStr;
- }
- char* RsaCrypto::fromBase64(string str)
- {
- int length = str.size();
- BIO* bs64 = BIO_new(BIO_f_base64());
- BIO* mem = BIO_new_mem_buf(str.data(), length);
- BIO_push(bs64, mem);
- char* buffer = new char[length];
- memset(buffer, 0, length);
- BIO_read(bs64, buffer, length);
- BIO_free_all(bs64);
- return buffer;
- }
复制代码 要想表明清晰这个模块,照旧要介绍一下里面一些比较陌生的api,如下:
- #include <openssl/rsa.h>
- // 申请一块内存, 存储了公钥和私钥
- // 如果想得到RSA类型变量必须使用 RSA_new();
- RSA *RSA_new(void);
- void RSA_free(RSA *);
- BIGNUM* BN_new(void);
- void BN_free(BIGNUM*);
- // 生成密钥对, 密钥对存储在内存中
- int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e, BN_GENCB *cb);
- 参数:
- - rsa: 通过RSA_new()获得
- - bits: 秘钥长度, 单位: bit, 常用的长度 1024*n (n正整数)
- - e: 比较大的数(5位以内)
- - 通过 BN_new 得到对应的变量
- - 初始化: BN_set_word(e, 12345);
- - cb: 回调函数, 用不到, 直接写NULL
- // rsa公钥私钥类型是一样的: RSA类型
- // 将参数rsa中的公钥提取出来
- RSA *RSAPublicKey_dup(RSA *rsa);
- - rsa参数: 秘钥信息
- - 返回值: rsa公钥
- // 将参数rsa中的私钥提取出来
- RSA *RSAPrivateKey_dup(RSA *rsa);
- - rsa参数: 秘钥信息
- - 返回值: rsa私钥
- // 创建bio对象
- // 密钥对写磁盘文件的时候, 需要编码 -> base64
- // 封装了fopen
- BIO *BIO_new_file(const char *filename, const char *mode);
- 参数:
- - filename: 文件名
- - mode: 文件打开方式和fopen打开方式的指定相同
-
- int PEM_write_bio_RSAPublicKey(BIO* bp, const RSA* r);
- int PEM_write_bio_RSAPrivateKey(BIO* bp, const RSA* r, const EVP_CIPHER* enc,
- unsigned char* kstr, int klen, pem_password_cb *cb, void* u);
- RSA* PEM_read_bio_RSAPublicKey(BIO* bp, RSA** r, pem_password_cb *cb, void* u);
- RSA* PEM_read_bio_RSAPrivateKey(BIO* bp, RSA** r, pem_password_cb *cb, void* u);
- 参数:
- - bp: 通过BIO_new_file();函数得到该对象
- - r: 传递一个RSA* rsa指针的地址, 传出参数-> 公钥/私钥
- - cb: 回调函数, 用不到, 指定为NULL
- - u: 给回调传参, 用不到, 指定为NULL
- //
- //
- RSA* PEM_read_RSAPublicKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
- RSA* PEM_read_RSAPrivateKey(FILE* fp, RSA** r, pem_password_cb *cb, void* u);
- // 写入文件中的公钥私钥数据不是原始数据, 写入的编码之后的数据
- // 是一种pem的文件格式, 数据使用base64进行编码
- int PEM_write_RSAPublicKey(FILE* fp, const RSA* r);
- int PEM_write_RSAPrivateKey(FILE* fp, const RSA* r, const EVP_CIPHER* enc,
- unsigned char* kstr, int klen, pem_password_cb *cb, void* u);
- 参数:
- - fp: 需要打开一个磁盘文件, 并且指定写权限
- - r: 存储了密钥对
- - 私钥独有的参数
- - enc: 指定的加密算法 -> 对称加密 -> NULL
- - kstr: 对称加密的秘钥 -> NULL
- - klen: 秘钥长度 -> 0
- - cb: 回调函数, 用不到, NULL
- - u: 给回调传参, 用不到, NULL
复制代码 以是整个代码块看下来重要完成了这几个任务:天生密钥对、公钥加密、私钥解密、数据署名、验证署名。至于toBase64和fromBase64就是为相识决我们的加密后的密钥在传输过程中解决的一些问题,这都是后话了,以是团体来看,这段代码可以抽象至如下表示:
- class MyRSA
- {
- public:
- MyRSA();
- ~MyRSA;
- // 生成密钥对
- // 公钥加密
- // 私钥解密
- // 数据签名
- // 验证签名
- private:
- RSA* pubkey;
- RSA* pirKey;
- }
复制代码 四、关于hsah模块的阐明
我们在这个项目中其实还封装了一个hash模块来调用,不过那是到最后封装外联接口支持网点之间直接用aes加解密的情况了,以是我们也是放到之后在做记载。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |