开启辅助访问 切换到宽版

精易论坛

 找回密码
 注册

QQ登录

只需一步,快速开始

用微信号发送消息登录论坛

新人指南 邀请好友注册 - 我关注人的新帖 教你赚取精币 - 每日签到


求职/招聘- 论坛接单- 开发者大厅

论坛版规 总版规 - 建议/投诉 - 应聘版主 - 精华帖总集 积分说明 - 禁言标准 - 有奖举报

查看: 845|回复: 15
收起左侧

[其它源码] c++ 调用 openssl 3.0 生成密钥对, 加解密

[复制链接]

结帖率:100% (8/8)
发表于 2024-4-5 17:56:39 | 显示全部楼层 |阅读模式   广西壮族自治区崇左市
分享源码
界面截图: -
是否带模块: -
备注说明: -
本帖最后由 福仔 于 2024-4-5 18:09 编辑

好像代码区里的#include <xxx>被markdown解析了, 编辑的时候#include都是能看见的
发布出来就看不见了, 这里多写一份吧
#include <string>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>

好久之前找过一次资料, 然后代码不知道丢哪了, 今天找了大半天才找到怎么生成, 悲剧
发个贴备份一下  


下面这段代码可以自己写到一个头文件里, 以后需要加解密就引用他就行  


#include <string>
#include 
#include 
#include 

namespace openssl_rsa_3_x
{
    // 官方文档 https://www.openssl.org/docs/man3.1/man7/EVP_PKEY-RSA.html
    // 下面的 GenerateKeys1/2/3, 都能用, 1和2效果是一样的, 3可以设置参数生成密钥

    // 从EVP_PKEY对象中提取公钥和私钥
    inline void GenerateKeysForEvp(EVP_PKEY* pkey, std::string& pri_key, std::string& pub_key)
    {
        BIO* pri = BIO_new(BIO_s_mem());
        BIO* pub = BIO_new(BIO_s_mem());

        PEM_write_bio_PrivateKey(pri, pkey, NULL, NULL, 0, NULL, NULL);
        PEM_write_bio_PUBKEY(pub, pkey);

        size_t pri_len = BIO_pending(pri);
        size_t pub_len = BIO_pending(pub);

        pri_key.resize(pri_len);
        pub_key.resize(pub_len);
        BIO_read(pri, &pri_key[0], pri_len);
        BIO_read(pub, &pub_key[0], pub_len);

        // 内存释放  
        BIO_free_all(pub);
        BIO_free_all(pri);
    }

    // 生成密钥对, 返回是否成功
    // pri_key = 接收私钥结果
    // pub_key = 接收公钥结果
    // bits = 密钥长度
    inline bool GenerateKeys1(std::string& pri_key, std::string& pub_key, UINT bits = 2048)
    {
        EVP_PKEY* pkey = EVP_RSA_gen(bits);
        if (!pkey)
            return false;

        GenerateKeysForEvp(pkey, pri_key, pub_key);

        EVP_PKEY_free(pkey);
        return true;
    }

    // 生成密钥对, 返回是否成功
    // pri_key = 接收私钥结果
    // pub_key = 接收公钥结果
    // bits = 密钥长度
    inline bool GenerateKeys2(std::string& pri_key, std::string& pub_key, UINT bits = 2048)
    {
        EVP_PKEY* pkey = NULL;
        EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
        EVP_PKEY_keygen_init(pctx);
        EVP_PKEY_generate(pctx, &pkey);
        if (!pkey)
            return false;

        GenerateKeysForEvp(pkey, pri_key, pub_key);

        EVP_PKEY_CTX_free(pctx);
        return true;
    }

    // 生成密钥对, 返回是否成功
    // pri_key = 接收私钥结果
    // pub_key = 接收公钥结果
    // bits = 密钥长度
    inline bool GenerateKeys3(std::string& pri_key, std::string& pub_key, UINT bits = 2048)
    {
        unsigned int primes = 3;
        OSSL_PARAM params[3];
        EVP_PKEY* pkey = NULL;
        EVP_PKEY_CTX* pctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);

        EVP_PKEY_keygen_init(pctx);

        // 设置参数生成密钥
        // 官方文档 https://www.openssl.org/docs/man3.0/man3/OSSL_PARAM_construct_uint.html
        params[0] = OSSL_PARAM_construct_uint("bits", &bits);
        params[1] = OSSL_PARAM_construct_uint("primes", &primes);
        params[2] = OSSL_PARAM_construct_end();
        EVP_PKEY_CTX_set_params(pctx, params);

        EVP_PKEY_generate(pctx, &pkey);

        if (!pkey)
        {
            EVP_PKEY_CTX_free(pctx);
            return false;
        }

        GenerateKeysForEvp(pkey, pri_key, pub_key);

        EVP_PKEY_CTX_free(pctx);
        return true;
    }

    // 一般情况下都是公钥加密, 私钥解密, 如果是私钥加密的话, 私钥也可以解密
    // 所以只写公钥加密和私钥解密的函数
    // 需要私钥解密公钥解密的话, 可以改 PEM_read_bio_PUBKEY/PEM_read_bio_PrivateKey
    // 看名字就能看出来, 一个是读取公钥, 一个是读取私钥
    // 后续的加解密操作都是根据 PEM_read_bio_PUBKEY/PEM_read_bio_PrivateKey 返回的对象操作的

    // 公钥加密
    // pData = 要加密的数据
    // nSize = 要加密的数据长度
    // pub_key = 公钥
    inline std::string rsa_encrypt(LPCVOID pData, size_t nSize, LPCSTR pub_key)
    {
        std::string en_data;
        if (!pData || nSize == 0)
            return en_data;   // 没有数据, 直接返回空字符串

        EVP_PKEY_CTX* ctx = 0;
        BIO* keybio = BIO_new_mem_buf(pub_key, -1);
        EVP_PKEY* pKey = PEM_read_bio_PUBKEY(keybio, 0, NULL, NULL);
        while (pKey)
        {
            ctx = EVP_PKEY_CTX_new(pKey, NULL);
            if (!ctx)
                break;

            if (EVP_PKEY_encrypt_init(ctx) <= 0)  // 初始化加密
                break;

            const int key_len = EVP_PKEY_size(pKey);
            char* buf = new char[key_len];

            LPCBYTE ptr = reinterpret_cast<LPCBYTE>(pData);
            LPCBYTE pEnd = ptr + nSize;
            const int block_len = key_len - 11;
            int len = block_len;

            // 计算结果长度, 预先分配这么大的内存, 计算方式为:
            // 每次加密最大尺寸 = block_len
            // 加密次数 = 总尺寸 / block_len
            // 每次加密得到的长度 = key_len
            // 总尺寸 = 每次加密得到的长度 * 总次数
            const size_t reserve_size = ((nSize / block_len + ((nSize % block_len == 0) ? 0 : 1)) * key_len);
            en_data.reserve(reserve_size);

            while (ptr < pEnd)
            {
                if ((ptr + len) > pEnd)
                    len = pEnd - ptr;

                size_t outl = block_len;
                int ret = EVP_PKEY_encrypt(ctx, reinterpret_cast<LPBYTE>(buf), &outl, ptr, len);
                if (ret > 0)
                {
                    en_data.append(buf, outl);
                }
                ptr += len;
            }
            delete[] buf;

            break;
        }

        if (pKey)
            EVP_PKEY_free(pKey);
        if (ctx)
            EVP_PKEY_CTX_free(ctx);

        return en_data;

    }

    // 私钥解密
    // pData = 要加密的数据
    // nSize = 要加密的数据长度
    // pub_key = 私钥
    inline std::string rsa_decrypt(LPCVOID pData, size_t nSize, LPCSTR pri_key)
    {
        std::string de_data;
        if (!pData || nSize == 0)
            return de_data;   // 没有数据, 直接返回空字符串

        EVP_PKEY_CTX* ctx = 0;
        BIO* keybio = BIO_new_mem_buf(pri_key, -1);
        EVP_PKEY* pKey = PEM_read_bio_PrivateKey(keybio, 0, NULL, NULL);

        while (pKey)
        {
            ctx = EVP_PKEY_CTX_new(pKey, NULL);
            if (!ctx)
                break;

            if (EVP_PKEY_decrypt_init(ctx) <= 0)  // 初始化解密
                break;

            // 解密的长度就不计算了, 直接使用数据长度, 解密的尺寸肯定小于数据长度
            de_data.reserve(nSize);
            LPCBYTE ptr = reinterpret_cast<LPCBYTE>(pData);
            LPCBYTE pEnd = ptr + nSize;
            const int key_len = EVP_PKEY_size(pKey);
            int len = key_len;
            char* buf = new char[key_len + 1];

            while (ptr < pEnd)
            {
                if ((ptr + len) > pEnd)
                    len = pEnd - ptr;

                size_t outl = key_len;
                int ret = EVP_PKEY_decrypt(ctx, reinterpret_cast<LPBYTE>(buf), &outl, ptr, len);
#if defined(_DEBUG) || defined(DEBUG)
                if (ret <= 0)
                {
                    unsigned long err = ERR_get_error(); //获取错误号
                    char err_msg[1024] = { 0 };
                    ERR_error_string(err, err_msg); // 格式:error:errId:库:函数:原因
                    printf("err msg: err:%ld, msg:%s\n", err, err_msg);
                }
#endif
                if (ret > 0)
                {
                    de_data.append(buf, outl);
                }
                ptr += len;
            }
            delete[] buf;

            break;
        }

        if (pKey)
            EVP_PKEY_free(pKey);
        if (ctx)
            EVP_PKEY_CTX_free(ctx);

        return de_data;
    }

}

下面是测试代码, 自行引用上面的代码
openssl怎么安装可以用vcpkg安装
vcpkg install openssl:x86-windows-static
经过我的测试, vs安装路径中带有空格的话
vcpkg安装会失败, 花了一天重装vs发现的  

// 自行引用上面的代码, 可以写到头文件然后include
// 也可以直接粘贴上来
int main()
{
    std::string pri_key, pub_key;

    // 生成密钥对
    openssl_rsa_3_x::GenerateKeys1(pri_key, pub_key);

    // 公钥加密, 加密私钥这段数据
    std::string en_data = openssl_rsa_3_x::rsa_encrypt(pri_key.c_str(), pri_key.size(), pub_key.c_str());

    // 私钥解密
    std::string de_data = openssl_rsa_3_x::rsa_decrypt(en_data.c_str(), en_data.size(), pri_key.c_str());

    bool b = de_data == pri_key;

    std::cout << "解密后的值和加密前的值对比结果: " << b << std::endl;

    return 0;

}

评分

参与人数 2精币 +2 收起 理由
wa690602724 + 1 感谢分享,很给力!~
財財 + 1 感谢分享,很给力!~

查看全部评分


发表于 2024-4-10 12:36:55 | 显示全部楼层   湖南省岳阳市
感谢分享,很给力!~
回复 支持 反对

使用道具 举报

签到天数: 2 天

发表于 2024-4-9 20:08:52 | 显示全部楼层   四川省成都市
支持开源~!感谢分享
回复 支持 反对

使用道具 举报

结帖率:80% (4/5)

签到天数: 2 天

发表于 2024-4-7 21:23:55 | 显示全部楼层   山东省潍坊市
已经顶贴,感谢您对论坛的支持!
回复 支持 反对

使用道具 举报

签到天数: 5 天

发表于 2024-4-7 16:14:51 | 显示全部楼层   云南省昆明市
支持开源~!感谢分享
回复 支持 反对

使用道具 举报

结帖率:70% (7/10)

签到天数: 12 天

发表于 2024-4-7 10:22:28 | 显示全部楼层   河南省焦作市

宝贵经验啊,很值得学习
回复 支持 反对

使用道具 举报

结帖率:0% (0/2)

签到天数: 10 天

发表于 2024-4-6 10:05:32 | 显示全部楼层   广西壮族自治区玉林市
谢谢分享
回复 支持 反对

使用道具 举报

结帖率:100% (1/1)

签到天数: 4 天

发表于 2024-4-6 08:34:55 | 显示全部楼层   广东省深圳市
学习一下
回复 支持 反对

使用道具 举报

结帖率:100% (4/4)

签到天数: 12 天

发表于 2024-4-6 08:08:00 | 显示全部楼层   山东省淄博市
感谢分享
回复 支持 反对

使用道具 举报

结帖率:0% (0/1)

签到天数: 1 天

发表于 2024-4-6 01:22:01 | 显示全部楼层   浙江省杭州市
感谢分享。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 致发广告者

发布主题 收藏帖子 返回列表

sitemap| 易语言源码| 易语言教程| 易语言论坛| 诚聘英才| 易语言模块| 手机版| 广告投放| 精易论坛
拒绝任何人以任何形式在本论坛发表与中华人民共和国法律相抵触的言论,本站内容均为会员发表,并不代表精易立场!
论坛帖子内容仅用于技术交流学习和研究的目的,严禁用于非法目的,否则造成一切后果自负!如帖子内容侵害到你的权益,请联系我们!
防范网络诈骗,远离网络犯罪 违法和不良信息举报电话0663-3422125,QQ: 800073686,邮箱:800073686@b.qq.com
Powered by Discuz! X3.4 揭阳市揭东区精易科技有限公司 ( 粤ICP备12094385号-1) 粤公网安备 44522102000125 增值电信业务经营许可证 粤B2-20192173

快速回复 返回顶部 返回列表