2021-08-29

本文介绍了RSA非对称加密的基本原理,包括公钥和私钥的概念及其作用。同时,讨论了RSA在实际使用中遇到的长度限制和特殊字符问题,提出了解决方案,如分段加密和Base64编码。此外,提供了加密和解密的C++代码示例,并推荐了一个开源代码项目以供学习参考。
摘要由CSDN通过智能技术生成


前言


一、RSA的基本原理

1.非对称秘钥的概念

首先介绍传统的对称秘钥,所谓对称秘钥就是加密和解密的秘钥是一样的(给锁锁上和解锁的钥匙是同一把),优点是简单快速,但是安全性一般比较低,普通的DES加密已经不够安全,现在使用的大多是3DES和AES。
与对称秘钥相对的概念就是非对称秘钥(即加密和解密的秘钥不是同一把)。针对非对称秘钥的经典设计RSA算法,可以进行下面的形象解释:
假设有一把非常神奇的锁,它有两种钥匙,一种叫私钥(只有一把),一种叫公钥(可以有很多把),用私钥上锁后只能用公钥打开,用公钥上锁后只能用私钥打开。由此可以衍生出很多有趣的特性:
1.你可以公开你的公钥,(在对称秘钥中如果秘钥被第三者得知,信息就能被第三者解密),即使发送消息的人和第三者有相同的秘钥,第三者也不能解密信息。
2.用私钥加密的信息只能用公钥解密,由此可以衍生出一种特殊的作用–身份验证。在公布公钥时你需要将公钥广布于众,如果有人接收到了一个加密信息而且能用你的公钥解密,那么就能确定是你发送的信息而不是其他人伪造的(容易遭到重放攻击,因此还需要对签名进行其他保护,如使用消息的hash结果进行私钥签名)
3.私钥不能用来加密发送的信息本身(除非你想让所有有公钥的人都能解密信息)而用来进行签名。
4.由于RSA的加密解密算法比较复杂,对资源的消耗比较大,因此一般使用于加密短信息或者少数几次的长信息,如登录时使用RSA加密传送AES秘钥,之后的通信就使用AES算法加解密信息。

2.非对称秘钥的结构和算法

结构内容
公钥(D,N)
私钥(E,N)
秘钥对(E.D.N)

加密算法
密 文 = 明 文 E m o d N 密文=明文^{E}modN =EmodN
解密算法
明 文 = 密 文 D m o d N 明文=密文^{D}modN =DmodN

二、RSA的使用限制

1.长度限制

正是由于RSA算法中的取模运算,因此RSA对加密信息和解密信息有长度限制。(比如5mod3=2mod3)

2.特殊字符的限制

不同的设备对特殊字符有可能会有不同的处理方案,比如有些设备不支持unicode编码,有些设备会把特殊字符作为结束符等,而明文和密文中有可能存在特殊字符,如果不加处理,容易造成很多非预期的错误。

3.针对长度的解决方案

加密长明文时必须一段一段的加密,组合后发送,然后在解密时同样一段一段的解密再组合成明文。

4.针对特殊字符的解决方案

使用base64算法将输入转化成只包含可见ASCII字符的字符串序列,在传输中使用base64编码后的字符串,处理时再进行解码。

三、代码示例

//加密函数
bool MySsl::EncryptionPubclic(const char *plaintext, size_t size_in, unsigned char **crypttext, size_t &size_out, std::string key)
{
    RSA* rsa=RSA_new();
    BIO *keybio = BIO_new_mem_buf((unsigned char *)key.c_str(), -1);
    const std::string publickey1_flag="-----BEGIN RSA PUBLIC KEY-----";
    const std::string publickey8_flag="-----BEGIN PUBLIC KEY-----";
    if(std::strncmp(key.c_str(),publickey1_flag.c_str(),publickey1_flag.length())==0){
        rsa=PEM_read_bio_RSAPublicKey(keybio,NULL,NULL,NULL);
    }else if(std::strncmp(key.c_str(),publickey8_flag.c_str(),publickey8_flag.length())==0){
        rsa=PEM_read_bio_RSA_PUBKEY(keybio,NULL,NULL,NULL);
    }else{
        return false;
    }
    int rsa_len = RSA_size(rsa);
    int slice_max=rsa_len-11;//单次处理的长度,11是一个预留的空间,如果过小则安全性降低
    int nums=size_in/slice_max;

    if(size_in%slice_max!=0){
        nums+=1;
    }//计算总共需要进行的分段加密次数
    int all_len=rsa_len*nums;//生成的密文长度
    *crypttext = (unsigned char *)malloc(all_len);
    memset(*crypttext, 0, rsa_len);

    for(int i=0;i<nums-1;i++){
        if (RSA_public_encrypt(slice_max, (unsigned char *)plaintext+i*slice_max,*crypttext+rsa_len*i, rsa, RSA_PKCS1_PADDING) < 0){
            return false;
        }
    }
    if(size_in!=0){
        int end_len=size_in%slice_max;
        if(end_len==0) end_len=slice_max;
        if (RSA_public_encrypt(end_len, (unsigned char *)plaintext+(nums-1)*slice_max,*crypttext+rsa_len*(nums-1), rsa, RSA_PKCS1_PADDING) < 0){
            return false;
        }
    }
    size_out=all_len;
    BIO_free(keybio);
    RSA_free(rsa);
    return true;
}
//解密函数
bool MySsl::DecryptPrivate(const char *crypttext,size_t size_in,unsigned char **plaintext,size_t size_out,std::string key){
    RSA* rsa=RSA_new();
    BIO *keybio = BIO_new_mem_buf((unsigned char *)key.c_str(), -1);
    const std::string privatekey_flag="-----BEGIN RSA PRIVATE KEY-----";
    if(std::strncmp(key.c_str(),privatekey_flag.c_str(),privatekey_flag.length())==0){
        rsa=PEM_read_bio_RSAPrivateKey(keybio,NULL,NULL,NULL);
    }else{
        return false;
    }
    int rsa_len = RSA_size(rsa);
    int slice_max=rsa_len-11;
    int nums=size_in/rsa_len;

    *plaintext = (unsigned char *)malloc(size_out);
    std::memset(*plaintext, 0, size_out);

    for(int i=0;i<nums;i++){
        if (RSA_private_decrypt(rsa_len, (u_char *)crypttext+i*rsa_len, *plaintext+slice_max*i,rsa, RSA_PKCS1_PADDING) < 0){
        //     return false;
        }
    }
    RSA_free(rsa);
    return true;
}

四、开源代码指南

Crypto-Example
该项目中包含了AES,RSA的秘钥生成、秘钥保存和加解密以及base64编码,并且附带了测试代码,非常值得学习。
我的项目
其中的AES以及base64部分代码参考了上面的项目实现,RSA部分代码则为参考部分教学代码后改进(分段加解密部分),相比上面的项目能够直接处理没有进行base64编码的明文和密文(但是在传输过程中仍然需要进行base64编码,这里只是能够处理包含特殊字符的输入,例如需要直接加密数据结构)。该项目在持续改进中,欢迎关注。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值