openssl RSA 加解密

1.openssl 安装与更新

openssl1.1.1之后支持国密SM2,SM3,SM4,所以安装openssl1.1.1d,选择源码安装的方式,下载 openssl 源码包:https://www.openssl.org/source/openssl-1.1.1d.tar.gz 

[root@V-Ubuntu18-002 ~]# openssl version
OpenSSL 1.1.1d 10 Sep 2019
[root@V-Ubuntu18-002 ~]# which openssl
/usr/bin/openssl
[root@V-Ubuntu18-002 ~]# tar zxf openssl-1.1.1d.tar.gz -C /usr/local/
[root@V-Ubuntu18-002 ~]# cd /usr/local/openssl-1.1.0d/
[root@V-Ubuntu18-002 openssl-1.1.0d]# ./config --prefix=/opt/openssl --openssldir=/opt/openssl shared

# 参数解释:
--openssldir=/opt/openssl
安装目录,默认:/usr/local/ssl
 
--prefix=PREFIX
设置 lib、include、bin 目录,默认:/usr/local/ssl

shared
no-shared
是否生成动态连接库。
[root@V-Ubuntu18-002 openssl-1.1.0d]# make
[root@V-Ubuntu18-002 openssl-1.1.0d]# make install

# 添加新的openssl软链接
[root@V-Ubuntu18-002 openssl-1.1.0d]# ln -s /opt/openssl/bin/openssl /usr/bin/openssl
 
# 将新的库文件地址写入记录so库的配置文件
[root@V-Ubuntu18-002 openssl-1.1.0d]# echo "/opt/openssl/lib/" >> /etc/ld.so.conf
 
# 设置生效
[root@V-Ubuntu18-002 openssl-1.1.0d]# ldconfig

2.RSA加解密

RSA是一个非对称加密算法。简单说来,非对称加密算法就是说加密解密一个文件需要有两个密钥,一个用来加密,为公钥,一个用来解密,为私钥。证书可以用来授权公钥的使用。

生成一个密钥:

openssl genrsa -out test.key 1024

这里-out指定生成文件的。需要注意的是这个文件包含了公钥和密钥两部分,也就是说这个文件即可用来加密也可以用来解密。后面的1024是生成密钥的长度。

openssl可以将这个文件中的公钥提取出来:

openssl rsa -in test.key -pubout -out test_pub.key

-in指定输入文件,-out指定提取生成公钥的文件名。至此,我们手上就有了一个公钥,一个私钥(包含公钥)。现在可以将用公钥来加密文件了。

我在目录中创建一个hello的文本文件,然后利用此前生成的公钥加密文件

openssl rsautl -encrypt -in hello -inkey test_pub.key -pubin -out hello.en 

-in指定要加密的文件,-inkey指定密钥,-pubin表明是用纯公钥文件加密,-out为加密后的文件。

解密文件:

openssl rsautl -decrypt -in hello.en -inkey test.key -out hello.de

-in指定被加密的文件,-inkey指定私钥文件,-out为解密后的文件。

3.RSA加解密一些问题

RSA密钥的三个重要大数:公钥指数e、私钥指数d和模值n,我们说的“密钥长度”一般只是指模值的位长度。目前主流可选值:1024位、2048位、3072位、4096位...。公钥指数是随意选的,但目前行业上公钥指数普遍选的都是65537(0x10001,5bits),该值是除了1、3、5、17、257之外的最小素数。私钥根据算法公式(ed%k=1,k=(p-1)(q-1))进行运算出来。那么私钥指数会是多少位?根据ed关系,私钥d=(x*k+1)/e,所以单看这个公式,私钥指数似乎也不是唯一结果,可能大于也可能小于1024bits的,但我们习惯上也是指某个小于1024bits的大整数。

明文长度

RSA实际可加密的明文长度最大是1024bits,如果小于这个长度就需要进行padding,因为如果没有padding,用户无法确分解密后内容的真实长度,字符串之类的内容问题还不大,以0作为结束符,但对二进制数据就很难理解,因为不确定后面的0是内容还是内容结束符。只要用到padding,那么就要占用实际的明文长度,我们一般使用的padding标准有NoPPadding、OAEPPadding、PKCS1Padding等,其中PKCS#1建议的padding占用了11个字节。128字节(1024bits)-减去11字节是117字节,对于RSA加密来讲,padding也是参与加密的,所以,依然按照1024bits去理解,但实际的明文只有117字节了。

密文长度

加密后的密文位长跟密钥的位长度是相同的。

4.例子

下面是一个例子,这个例子利用已有的密钥文件来对source字符串进行加密与解密:

#include<stdio.h>
 2 #include<stdlib.h>
 3 #include<string.h>
 4 #include<openssl/rsa.h>
 5 #include<openssl/pem.h>
 6 #include<openssl/err.h>
 7 #define OPENSSLKEY "test.key"
 8 #define PUBLICKEY "test_pub.key"
 9 #define BUFFSIZE 1024
10 char* my_encrypt(char *str,char *path_key);//加密
11 char* my_decrypt(char *str,char *path_key);//解密
12 int main(void)
{
13     char *source="i like dancing !";
14     char *ptr_en,*ptr_de;
15     printf("source is    :%s\n",source);
16     ptr_en=my_encrypt(source,PUBLICKEY);
17     printf("after encrypt:%s\n",ptr_en);
18     ptr_de=my_decrypt(ptr_en,OPENSSLKEY);
19     printf("after decrypt:%s\n",ptr_de);
20     if(ptr_en!=NULL){
21         free(ptr_en);
22     }   
23     if(ptr_de!=NULL){
24         free(ptr_de);
25     }   
26     return 0;
27 }
28 char *my_encrypt(char *str,char *path_key){
29     char *p_en;
30     RSA *p_rsa;
31     FILE *file;
32     int flen,rsa_len;
33     if((file=fopen(path_key,"r"))==NULL){
34         perror("open key file error");
35         return NULL;    
36     }   
37     if((p_rsa=PEM_read_RSA_PUBKEY(file,NULL,NULL,NULL))==NULL){
38     //if((p_rsa=PEM_read_RSAPublicKey(file,NULL,NULL,NULL))==NULL){   换成这句死活通不过,无论是否将公钥分离源文件
39         ERR_print_errors_fp(stdout);
40         return NULL;
41     }   
42     flen=strlen(str);
43     rsa_len=RSA_size(p_rsa);
44     p_en=(unsigned char *)malloc(rsa_len+1);
45     memset(p_en,0,rsa_len+1);
46     if(RSA_public_encrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_en,p_rsa,RSA_NO_PADDING)<0){
47         return NULL;
48     }
49     RSA_free(p_rsa);
50     fclose(file);
51     return p_en;
52 }
53 char *my_decrypt(char *str,char *path_key){
54     char *p_de;
55     RSA *p_rsa;
56     FILE *file;
57     int rsa_len;
58     if((file=fopen(path_key,"r"))==NULL){
59         perror("open key file error");
60         return NULL;
61     }
62     if((p_rsa=PEM_read_RSAPrivateKey(file,NULL,NULL,NULL))==NULL){
63         ERR_print_errors_fp(stdout);
64         return NULL;
65     }
66     rsa_len=RSA_size(p_rsa);
67     p_de=(unsigned char *)malloc(rsa_len+1);
68     memset(p_de,0,rsa_len+1);
69     if(RSA_private_decrypt(rsa_len,(unsigned char *)str,(unsigned char*)p_de,p_rsa,RSA_NO_PADDING)<0){
70         return NULL;
71     }
72     RSA_free(p_rsa);
73     fclose(file);
74     return p_de;
75 }

以上创建公钥是从文件读取,也可以从字符串读取创建公钥私钥,并加解密。

int main(int argc, char *argv[])
{
    std::string rsaPriKeyStr="MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAM/HrYzJv8fyngk0Pxt2zln0SoW4DJB+PwMLxYkNnh8dfjZliqFTF/EfMj8qSgVHcLGkUfZcx+9vabcEzNDbfyNA+62mokVimoKBEhmZG6qWmxuQ60K/vJmNVPKbdUrSw04Ye/dJeY3J/O4wdHGcPquBlkH7Or+6Rpmb7UUiGVCzAgMBAAECgYA69dmMmu9d6L3ptLa3r2Xs5sKC4XBHTFvLHHsIYiHdiHv5KaVGFBM2K/kRLYnlp5kV/5/2feuKSR5Bfg40siSINIMX51oOxIIF4G/E8BaCkmYKHuklwx060BjUven4AY+kPYZhga4moUTueWxh+tUIQuubTiG9EriUDJObFV2WqQJBAO3YZ1Mh7wHhljb1scCdq7ucB6VJk0LP0ecSamCXC79VareZm69P5qisvH55Vspq6eth+GRAkdwzmQbDG6JV6x0CQQDfo8kC4Fnr7HFM/ivNRFmA61SpOOtoA9afOwSwVaMXA19LbcMiBU6nBnD1E6Eu7JaR16Q2c6wNDCZvpO3PwpIPAkEA1v/p1uIz1XWAfkn21nEwzjSrTKSHs7t8uO6SrqGbgufJwhHfA8isbwlGkuVIqU2gCqDTEnyz3WPfDpfMISCdmQJATABdT5zu8j5oQsIeIKQZxrYzD09IsRCc34AvF74Crs92kyu6JNWo5RpLSP5iyI+em/Gs3NRyo/x9E3P5XaHCAQJAcR3J6yRU2NG87eCwE5W0O0XOUeD78ooSGxovxyN+90BIxLMu34KS/7HroxE5hsfrao83gV7DG3nhkXW6J+ZXzw==";
    std::string rsaPubKeyStr="MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPx62Myb/H8p4JND8bds5Z9EqFuAyQfj8DC8WJDZ4fHX42ZYqhUxfxHzI/KkoFR3CxpFH2XMfvb2m3BMzQ238jQPutpqJFYpqCgRIZmRuqlpsbkOtCv7yZjVTym3VK0sNOGHv3SXmNyfzuMHRxnD6rgZZB+zq/ukaZm+1FIhlQswIDAQAB";
int nPriKeyLen = rsaPriKeyStr.size();
unsigned char source[20]={0x41,0x12,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x10, 0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x20};
unsigned char rsa_en[512],rsa_de[512];
int rsaenStrLen,rsadeStrLen;
//添加pem编码头,BEGIN RSA PRIVATE KEY是 PKCS#1,BEGIN PRIVATE KEY
    for(int i = 64; i < rsaPriKeyStr.size(); i+=64)
    {
        if(rsaPriKeyStr[i] != '\n')
        {
            rsaPriKeyStr.insert(i, "\n");
        }
        ++i;
    }
    rsaPriKeyStr.insert(0, "-----BEGIN PRIVATE KEY-----\n");
    rsaPriKeyStr.append("\n-----END PRIVATE KEY-----\n");
    int nPubKeyLen = rsaPubKeyStr.size();
    for(int i = 64; i < rsaPubKeyStr.size(); i+=64)
    {
        if(rsaPubKeyStr[i] != '\n')
        {
            rsaPubKeyStr.insert(i, "\n");
        }
        ++i;
    }
    rsaPubKeyStr.insert(0, "-----BEGIN PUBLIC KEY-----\n");
    rsaPubKeyStr.append("\n-----END PUBLIC KEY-----\n");
    //加密
    rsaenStrLen=my_rsaencrypt(rsaPubKeyStr,source,20,rsa_en);
    printf("rsaenStrLen :%d \n",rsaenStrLen);
    printf("rsa_en:");
    for(int i=0;i<rsaenStrLen;i++)
    {
        printf("0x%x ",rsa_en[i]);
    }
    printf("\n");
    //解密
    rsadeStrLen=rsadeop->dencryptStr(rsaPriKeyStr,rsa_en,rsaenStrLen,rsa_de);
    printf("rsadeStrLen :%d \n",rsadeStrLen);
    printf("rsa_de: ");
    for(int i=0;i<rsadeStrLen;i++)
    {
        printf("0x%x ",rsa_de[i]);
    }
     printf("\n");
}
  
int my_rsaencrypt(std::string keystr,unsigned char *sourStr,int len,unsigned char* enStr)
{
       char* p_en;
       RSA *p_rsa;
       int rsa_len;

       BIO* pubBp = NULL;
       std::string temPubKeyStr =keystr;
       //creaete pub key
       char *chPublicKey = const_cast<char *>(temPubKeyStr.c_str());
       if ((pubBp = BIO_new_mem_buf(chPublicKey, -1)) == NULL)
       {
           printf("BIO_new_mem_buf failed!\n");

       }

       p_rsa = PEM_read_bio_RSA_PUBKEY(pubBp, NULL, NULL, NULL);
       BIO_free_all(pubBp);
       if (NULL == p_rsa)
       {
           ERR_load_crypto_strings();
           char errBuf[512];
           ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
           printf("load RSA public key failed[%s]\n", errBuf);
       }
       rsa_len=RSA_size(p_rsa);
       p_en=( char*)malloc(rsa_len+1);
       memset(p_en,0,rsa_len+1);
       int deLen=RSA_public_encrypt(len,(unsigned char*)sourStr,(unsigned char*)p_en,p_rsa,RSA_PKCS1_PADDING); //RSA_PKCS1_PADDING解密填充方式需要对应,NO_PADDING模式没办法确定密钥长度
       if(deLen<0)
       {
           return NULL;
       }
       memcpy(enStr,p_en,deLen);
       RSA_free(p_rsa);
       return deLen;
}

int dencryptStr(std::string rsaPriKeyStr,unsigned char * cEnStr,int cEnstrLen,unsigned char* deStr)
{
    RSA * mRsaPriKey;
    BIO* priBp = NULL;
    char *cmDeStr = NULL;
    int rsalen;
    //创建私钥
    char *chPrilicKey = const_cast<char *>(rsaPriKeyStr.c_str());
    if ((priBp = BIO_new_mem_buf(chPrilicKey, -1)) == NULL)
    {
        printf( "BIO_new_mem_buf failed!");
    }
    mRsaPriKey = PEM_read_bio_RSAPrivateKey(priBp, NULL, NULL, NULL);
    BIO_free_all(priBp);
    if (NULL == mRsaPriKey)
    {
        //ERR_load_crypto_strings();
        char errBuf[512];
        ERR_error_string_n(ERR_get_error(), errBuf, sizeof(errBuf));
        printf("load RSA public key failed[%s]\n", errBuf);
    }
    //解密
    rsalen=RSA_size(mRsaPriKey);
    cmDeStr=(char*)malloc(rsalen+1);
    memset(cmDeStr,0,rsalen+1);
    int delen=RSA_private_decrypt(enlen, reinterpret_cast<unsigned char *>(cEnStr), reinterpret_cast<unsigned char *>(cmDeStr),mRsaPriKey,RSA_PKCS1_PADDING);//加解密填充模式需要确定
    if(delen<0)
    {
        LOG(INFO) <<" RSA_private_decrypt failed!";
        free(cmDeStr);
        return 0;

    }
    memcpy(cDeStr,cmDeStr,delen);
    free(cmDeStr);
    return delen;
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值