openssl RSA 加解密
时间:2022-11-05 08:30:01
1.openssl 安装与更新
openssl1.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但是,我们习惯上也指一个小于1024的,bits的大整数。
明文长度
RSA实际可加密的明文长度最大为1024bits,如果小于此长度,则需要进行padding,因为如果没有padding,用户无法确定解密内容的真实长度,字符串等内容问题不大,以0为结束符,但很难理解二进制数据,因为不确定以下0是内容还是内容结束符。只要用到padding,我们通常使用它来占用实际的明文长度padding标准有NoPPadding、OAEPPadding、PKCS1Padding等,其中PKCS#1建议的padding占11个字节。128字节(1024bits)-为117字节减去11字节RSA加密来讲,padding也参与加密,所以还是按照1024bits理解,但实际明文只有117字节。
密文长度
加密后的密文位长与密钥的位长相同。
4.例子
下面是一个使用现有密钥文件的例子source加密和解密字符串:
#include 2 #include 3 #include 4 #include 5 #include 6 #include 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;idencryptStr(rsaPriKeyStr,rsa_en,rsaenStrLen,rsa_de);
printf("rsadeStrLen :%d \n",rsadeStrLen);
printf("rsa_de: ");
for(int i=0;i(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(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(cEnStr), reinterpret_cast(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;
}