非对称加密、解密原理及openssl中的RSA示例代码

2023-09-14 19:53:08

一、【原理简介】非对称加密

非对称加密,也被称为公钥加密,其中使用一对相关的密钥:一个公钥和一个私钥。公钥用于加密数据,私钥用于解密数据。公钥可以公开分享,而私钥必须保密。

  1. 密钥生成:

    • 当一个用户或设备希望使用非对称加密时,要生成一对密钥:一个公钥和一个私钥。这两个密钥是数学上相关的,但从公钥中计算出私钥在计算上是不可行的。
  2. 加密过程:

    • 发送方使用接收方的公钥对消息进行加密。只有持有与那个公钥相对应的私钥的人(在这种情况下是接收方)才能解密这个消息。
  3. 解密过程:

    • 接收方使用其私钥对接收到的加密消息进行解密,以恢复原始消息。
  4. 安全性:

    • 即使攻击者知道公钥并且他们拦截了加密的消息,但由于他们没有私钥,所以他们不能解密那个消息。这提供了保密性。
    • 由于私钥不是公开的,因此无法用它来伪造加密的消息。
  5. 数字签名:

    • 除了保密性外,非对称加密还可以用于数字签名。发送方使用其私钥对消息的哈希进行加密,产生一个数字签名。
    • 接收方可以使用发送方的公钥来验证签名,确保消息是从发送方发出的,并且没有被修改。
  6. 主要算法:

    • RSA、ElGamal、ECC(椭圆曲线加密)是非对称加密的主要算法。
  7. 性能问题:

    • 与对称加密相比,非对称加密通常计算上更加昂贵,所以它在处理大量数据时可能不太实用。在实际场景中,经常使用非对称加密来交换一个对称密钥(如AES密钥),然后使用对称加密来加密实际的数据。
  8. 公钥基础设施(PKI):

    • PKI是一个结合硬件、软件、策略和标准,以实现公钥加密和数字签名服务的体系结构。它包括证书颁发机构(CA),负责颁发和验证数字证书。

总结:非对称加密使用一对密钥来确保数据的安全性和完整性。公钥用于加密,而私钥用于解密或签名。


二、【代码】生成秘钥对文件 和 读取秘钥对加密、加密

  • example 1 生成秘钥对
#include <stdio.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>

/* compile : gcc createRSA_KeyPair.c -I./include -L./lib -lcrypto -Wl,-rpath=./lib  */
int main() {
    int ret = 0;
    RSA *r = NULL;
    BIGNUM *bne = NULL;
    int bits = 2048;
    unsigned long e = RSA_F4;

    // 1. 生成 RSA 密钥对
    bne = BN_new();
    ret = BN_set_word(bne, e);
    if(ret != 1) {
        goto free_all;
    }

    r = RSA_new();
    ret = RSA_generate_key_ex(r, bits, bne, NULL);
    if(ret != 1) {
        goto free_all;
    }

    // 2. 保存私钥到 PEM 文件
    FILE *fp = fopen("private_key.pem", "wb");
    if(fp == NULL) {
        perror("Unable to open private key file for writing");
        goto free_all;
    }
    PEM_write_RSAPrivateKey(fp, r, NULL, NULL, 0, NULL, NULL);
    fclose(fp);

    // 3. 保存公钥到 PEM 文件
    fp = fopen("public_key.pem", "wb");
    if(fp == NULL) {
        perror("Unable to open public key file for writing");
        goto free_all;
    }
    PEM_write_RSA_PUBKEY(fp, r);
    fclose(fp);

free_all:
    RSA_free(r);
    BN_free(bne);

    return 0;
}
  • example 2 使用秘钥对加密、解密
#include <stdio.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>

/* compile : gcc keyFromFile.c -I./include -L./lib -lcrypto -Wl,-rpath=./lib  */
int main() {
    // 加载公钥
    FILE* pubKeyFile = fopen("public_key.pem", "rb");
    RSA* rsaPublicKey = PEM_read_RSA_PUBKEY(pubKeyFile, NULL, NULL, NULL);
    fclose(pubKeyFile);

    // 加载私钥
    FILE* privKeyFile = fopen("private_key.pem", "rb");
    RSA* rsaPrivateKey = PEM_read_RSAPrivateKey(privKeyFile, NULL, NULL, NULL);
    fclose(privKeyFile);

    const char* plainText = "Hello, OpenSSL!";
    char encrypted[4098] = {};
    char decrypted[4098] = {};

    // 使用公钥加密
    int encryptedLength = RSA_public_encrypt(strlen(plainText), (unsigned char*)plainText,
                                             (unsigned char*)encrypted, rsaPublicKey, RSA_PKCS1_OAEP_PADDING);
    if (encryptedLength == -1) {
        printf("Public Encrypt failed \n");
        return 0;
    }

    // 使用私钥解密
    int decryptedLength = RSA_private_decrypt(encryptedLength, (unsigned char*)encrypted,
                                              (unsigned char*)decrypted, rsaPrivateKey, RSA_PKCS1_OAEP_PADDING);
    if (decryptedLength == -1) {
        printf("Private Decrypt failed \n");
        return 0;
    }
    decrypted[decryptedLength] = '\0';

    printf("Original: %s\n", plainText);
    printf("Decrypted: %s\n", decrypted);

    RSA_free(rsaPublicKey);
    RSA_free(rsaPrivateKey);

    return 0;
}

三、【代码】代码内生成秘钥对并加密解密

  • c风格
#include <stdio.h>
#include <string.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>

/* compile : gcc RSA.c -I./include -L./lib -lcrypto -Wl,-rpath=./lib */
int main() {
    // 1. create key
    int ret = 0;
    RSA *rsa = NULL;
    BIGNUM *bne = NULL;
    int bits = 2048;
    unsigned long e = RSA_F4;
    bne = BN_new();
    ret = BN_set_word(bne, e);
    if(ret != 1) {
        goto free_all;
    }

    rsa = RSA_new();
    ret = RSA_generate_key_ex(rsa, bits, bne, NULL);
    if(ret != 1) {
        goto free_all;
    }

    // 2. encrypto
    const char* plainText = "Hello, OpenSSL!";
    char encrypted[4098] = {};
    int encryptedLength = RSA_public_encrypt(strlen(plainText) + 1, (unsigned char*)plainText,
                                             (unsigned char*)encrypted, rsa, RSA_PKCS1_OAEP_PADDING);
    if(encryptedLength == -1) {
        printf("Public Encrypt failed \n");
        goto free_all;
    }

    // 3. decrypto
    char decrypted[4098] = {};
    int decryptedLength = RSA_private_decrypt(encryptedLength, (unsigned char*)encrypted,
                                              (unsigned char*)decrypted, rsa, RSA_PKCS1_OAEP_PADDING);
    if(decryptedLength == -1) {
        printf("Private Decrypt failed \n");
        goto free_all;
    }
    decrypted[decryptedLength] = '\0';

    printf("Original: %s\n", plainText);
    printf("Decrypted: %s\n", decrypted);

free_all:
    RSA_free(rsa);
    BN_free(bne);

    return 0;
}
  • C++风格
#include <iostream>
#include <vector>
#include <stdexcept>

extern "C"{
#include <openssl/pem.h>
#include <openssl/rsa.h>
}

class RSAWrapper {
public:
    RSAWrapper() {
        rsa = RSA_new();
        bne = BN_new();
        BN_set_word(bne, RSA_F4);
        RSA_generate_key_ex(rsa, 2048, bne, nullptr);
    }

    ~RSAWrapper() {
        RSA_free(rsa);
        BN_free(bne);
    }

    std::vector<unsigned char> encrypt(const std::string &plainText) {
        std::vector<unsigned char> encrypted(RSA_size(rsa));
        int encryptLength = RSA_public_encrypt(plainText.size(),
                                               reinterpret_cast<const unsigned char*>(plainText.c_str()),
                                               encrypted.data(),
                                               rsa,
                                               RSA_PKCS1_OAEP_PADDING);
        if (encryptLength == -1) {
            throw std::runtime_error("Error during encryption");
        }
        return encrypted;
    }

    std::string decrypt(const std::vector<unsigned char> &cipherText) {
        std::vector<unsigned char> decrypted(RSA_size(rsa));
        int decryptLength = RSA_private_decrypt(cipherText.size(),
                                                cipherText.data(),
                                                decrypted.data(),
                                                rsa,
                                                RSA_PKCS1_OAEP_PADDING);
        if (decryptLength == -1) {
            throw std::runtime_error("Error during decryption");
        }
        return std::string(reinterpret_cast<char*>(decrypted.data()));
    }

private:
    RSA *rsa;
    BIGNUM *bne;
};


/* compile : g++ -std=c++11  RSA.cpp -I./include -L./lib -lcrypto -Wl,-rpath=./lib */
int main() {
    try {
        RSAWrapper rsaWrapper;
        std::string plainText = "Hello, OpenSSL C++!";
        auto encrypted = rsaWrapper.encrypt(plainText);
        auto decrypted = rsaWrapper.decrypt(encrypted);

        std::cout << "Original Text: " << plainText << std::endl;
        std::cout << "Decrypted Text: " << decrypted << std::endl;

    } catch (const std::exception &e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }

    return 0;
}
更多推荐

xshell---git上传文件到gitee远程仓库配置

1.git下载如果没有xshell下没有下载过git,可以参考这篇的教程:Linux配置安装git详细教程下载后可以通过git--version查看git的版本号,验证是否安装成功2.新建仓库首先需要在gitee上注册一个账号然后再主页面点击右上边框的+号,选择新建仓库,建立一个仓库:然后填写新建仓库的名称,系统会根据

GIT使用需知,哪些操作会导致本地代码变动

系列文章目录手把手教你安装Git,萌新迈向专业的必备一步GIT命令只会抄却不理解?看完原理才能事半功倍!常用GIT命令详解,手把手让你登堂入室GIT实战篇,教你如何使用GIT可视化工具GIT使用需知,哪些操作会导致本地代码变动系列文章目录一、本地代码变动的本质1.远程跟踪分支2.贮藏区(stash)二、分支切换三、分支

(vue的入门

vue的入门一.Vue是什么二.Vue的特点及优势三.使用Vue的详细步骤四.Vue的基本语法五.Vue的生命周期一.Vue是什么Vue(发音为/“vjuː”/,类似于"view")是一套用于构建用户界面的渐进式JavaScript框架。它是一个开源的、轻量级的MVVM(模型-视图-视图模型)框架,专注于实现数据驱动的

vue +element 删除按钮操作 (删除单个数据 +删除页码处理 )

1.配置接口deleteItemById:"/api/goods/deleteItemById",//删除商品操作2.get请求接口//删除接口后台给我们返iddeleteItemById(params){returnaxios.get(base.deleteItemById,{params})}3.异步请求接口asy

如何在Gazebo中实现多机器人编队仿真

文章目录前言一、仿真前的配置二、实现步骤1.检查PC和台式机是否通讯成功2.编队中对单个机器人进行独立的控制3、对机器人进行编队控制前言实现在gazebo仿真环境中添加多个机器人后,接下来进行编队控制,对具体的实现过程进行记录。一、仿真前的配置本文的多机器人编队,在turtlebot3单个机器人的建图、导航等功能的基础

Linux设备驱动模型之SPI

Linux设备驱动模型之SPISPI:SerialPeripheralInterface,串行外设接口,主要用于控制器与外部传感器进行数据通信的接口,它是一种同步、全双工、主从式接口。SPI接口介绍接口定义SPI接口有4根信号线,分别是片选信号、时钟信号、串行输出数据线、串行输入数据线。SS:从设备使能信号,由SPI主

固定资产管理系统都有哪些功能呢

固定资产管理系统作为企业资产管理的重要工具,具有提高效率、降低成本、保证资产合理使用的多种功能。以下是一些典型的功能:资产登记和信息管理:系统可以自动记录公司的固定信息,包括资产名称、型号、购买日期、原始价值、折旧方法、折旧年限等。同时,系统还支持自动更新和查看资产信息。资产申请和偿还:员工可以通过平台申请或偿还资产,

什么是蓝葡萄酒,如何酿造?

有红色的葡萄酒、白色的葡萄酒和玫瑰色的葡萄酒,但你有听说过有些葡萄酒是蓝色的吗?什么是蓝葡萄酒,它来自哪里?蓝葡萄酒是用食用色素染色的普通葡萄酒,通常用添加剂增甜,大多数生产商用白葡萄来制作它,用红葡萄皮的色素来着色。由此产生的葡萄酒清淡爽口,带有明显的酸味、淡淡的甜味和果香。从法律角度来看蓝色的葡萄酒尽管它经历了酿酒

隧道代理的未来发展趋势和应用前景

在当今数字化时代,隧道代理作为一种重要的网络工具,为用户提供了绕过网络限制、保护隐私和增强安全性的方式。本文将探讨隧道代理的未来发展趋势和广阔应用前景,揭示其在不断变化的互联网环境中所扮演的关键角色。一、隧道代理的发展趋势更加智能化的代理技术:随着人工智能和机器学习的迅猛发展,隧道代理将更加智能化,能够自动适应不同的网

回调函数c++

C++回调函数的理解与使用一、回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。回调函数机制:1、定义一

780. 到达终点;2360. 图中的最长环;1871. 跳跃游戏 VII

780.到达终点核心思想:正难则反,如果从起点到终点很难想。那么我们就考虑从终点到起点,由于起点为正数,那么终点(x,y)的上一步一定是(x-y,y)或者(x,y-x)很明显肯定是大值减去小的值,然后我们不断重复这种操作,直到,终点的x或者y等于起点的x或者y,又或者两个值相同的时候停止,因为这种时候再去减去一个数一定

热文推荐