openssl rsa非对称加密
背景:
为了实现java与C之间的通信数据rsa加密,研究了rsa的加密方式。
java端使用代码生成的公钥、私钥文件格式都是PKCS#8格式的,使用openssl上面的命令生成的公钥、私钥都是PKCS1格式的,哪怕转成pkcs8格式的,java那边也解不出来,不知道为什么。
所以,最后使用的公钥、私钥都是java那边生成的,C语言这边使用的openssl接口不区分是pkcs1、pkcs8格式,都是支持的。
补充说明:
1、java生成的公私钥格式为pkcs8,而openssl默认生成的公私钥格式为pkcs1,两者的**实际上是不能直接互用的。
2、java采用的rsa默认补齐方式是pkcs1,因此互用的时候需要将openssl中的补齐方式设置为RSA_PKCS1_PADDING。
3、rsa加密中,加密数据长度有限制,不能超过**长度-11字节(如1024位的**,数据长度最长为117字节);密文长度为**的字节数(1024位的**,加密后的密文长度就是128字节)。
RSA加密常用的填充方式有下面3种:
1.RSA_PKCS1_PADDING 填充模式,最常用的模式
要求:
输入:必须 比 RSA 钥模长(modulus) 短至少11个字节, 也就是 RSA_size(rsa) – 11
如果输入的明文过长,必须切割,然后填充
输出:和modulus一样长
根据这个要求,对于512bit的**, block length = 512/8 – 11 = 53 字节
2.RSA_PKCS1_OAEP_PADDING
输入:RSA_size(rsa) – 41
输出:和modulus一样长
3.for RSA_NO_PADDING 不填充
输入:可以和RSA钥模长一样长,如果输入的明文过长,必须切割, 然后填充
输出:和modulus一样长
第一步:先下载openssl压缩包,通过openssl官网下载之后的压缩包为openssl-1.1.1h.tar.gz。解压,编译,安装,最后需要的是openssl-1.1.1h中的include头文件以及libcrypto.so,libssl.so动态库。
openssl官网:
https://www.openssl.org/source/
第二步:编码
/*rsaEncDec.h*/
#ifndef _RSAENCDEC_H_
#define _RSAENCDEC_H_
#include <stdio.h>
#include <string.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/evp.h>
#define MINLEN 128
#define MIDLEN 1024
#define MAXLEN 4096
/*java生成的公钥文件*/
const char PUBLIC_KEY_FILE[MINLEN] = "./publicKey.keystore";
/*java生成的私钥文件*/
const char PRIVATE_KEY_FILE[MINLEN] = "./privateKey.keystore";
void read_publickey_content(char* pubkey);
void PubKeyPEMFormat(char* pubkey);
void PublicEncrypt(char* in_plain, char* cipher);
void PrivKeyPEMFormat(char* privkey);
int Base64Encode(const char* encoded, int encodedLength, char* decoded);
int Base64Decode(const char* encoded, int encodedLength, char* decoded);
#endif // !_RSAENCDEC_H_
/*rsaEncDec.c*/
#include "rsaEncDec.h"
/****************************************************************
* function name : read_publickey_content
* functional description : 读取公钥文件内容
* input parameter : pubkey:存放读取公钥的内容
* output parameter : None
* return value: None
* history :
*****************************************************************/
void read_publickey_content(char* pubkey)
{
FILE* fp = fopen(PUBLIC_KEY_FILE, "rb");
if (fp == NULL)
{
printf("open file error\n");
return;
}
fread(pubkey, MIDLEN, 1, fp);
fclose(fp);
}
/****************************************************************
* function name : read_privatekey_content
* functional description : 读取私钥文件内容
* input parameter : pubkey:存放读取公钥的内容
* output parameter : None
* return value: None
* history :
*****************************************************************/
void read_privatekey_content(char* private_key)
{
FILE* fp = fopen(PRIVATE_KEY_FILE, "rb");
if (fp == NULL)
{
printf("open file error\n");
return;
}
fread(private_key, MIDLEN, 1, fp);
fclose(fp);
}
/****************************************************************
* function name : Base64Encode
* functional description : base64编码
* input parameter : encoded:存放编码后的数据;encodedLength:数据长度;decoded:原数据
* output parameter : None
* return value: None
* history :
*****************************************************************/
int Base64Encode(const char* encoded, int encodedLength, char* decoded)
{
return EVP_EncodeBlock((unsigned char*)decoded, (const unsigned char*)encoded, encodedLength);
}
/****************************************************************
* function name : Base64Decode
* functional description : base64解码
* input parameter : encoded:存放解码后的数据;encodedLength:数据长度;decoded:原数据
* output parameter : None
* return value: None
* history :
*****************************************************************/
int Base64Decode(const char* encoded, int encodedLength, char* decoded)
{
return EVP_DecodeBlock((unsigned char*)decoded, (const unsigned char*)encoded, encodedLength);
}
/****************************************************************
* function name : PubKeyPEMFormat
* functional description : 对公钥内容进行PEM格式化;没有BEGIN、END的话,就分段加上
* input parameter : pubkey:公钥数据
* output parameter : None
* return value: None
* history :
*****************************************************************/
void PubKeyPEMFormat(char* pubkey)
{
char format_pubkey[MAXLEN] = "";
char pub_tem[MAXLEN] = "";
char* pub_begin = "-----BEGIN PUBLIC KEY-----\n";
char* pub_end = "-----END PUBLIC KEY-----\n";
char* check = strstr(pubkey, pub_begin);
if (check)
{
return;
}
else
{
int nPublicKeyLen = strlen(pubkey);
int index = 0, publength = 0;
memcpy(format_pubkey, pub_begin, 27);
for (index = 0; index < nPublicKeyLen; index += 64)
{
memcpy(pub_tem, pubkey + index, 64);
strcat(format_pubkey, pub_tem);
publength = strlen(format_pubkey);
format_pubkey[publength] = '\n';
memset(pub_tem, 0, sizeof(pub_tem));
}
strcat(format_pubkey, pub_end);
memcpy(pubkey, format_pubkey, strlen(format_pubkey));
}
}
/****************************************************************
* function name : PublicEncrypt
* functional description : 公钥加密数据
* input parameter : in_plain:原文;cipher:加密数据
* output parameter : None
* return value: None
* history :
*****************************************************************/
void PublicEncrypt(char* in_plain, char* cipher)
{
/*公钥文件内容*/
char pubkey[MIDLEN] = "";
read_publickey_content(pubkey);
if (strlen(pubkey) == 0)
{
printf("get pubkey file content error");
return;
}
char plain[MAXLEN] = ""; /*存放分段后的每一段明文*/
char encrypted[MAXLEN] = ""; /*存放每一段明文的解密结果*/
char result[MAXLEN] = ""; /*存放拼接后的密文*/
char plain_rest[MAXLEN] = ""; /*存放分段之后剩余部分的明文*/
char encrypted_rest[MAXLEN] = ""; /*存放对剩余部分明文的解密结果*/
/*对公钥进行PEM格式化*/
PubKeyPEMFormat(pubkey);
/*根据公钥长度进行相关的计算*/
int pubKeyLen = strlen(pubkey); /*计算公钥长度*/
int CryLen = 1024; /***为1024位*/
int maxPlain = CryLen / 8 - 11; /*通过加密长度获取明文的最大加密长度*/
int cipherLen = CryLen / 8; /*通过加密长度获取密文的长度*/
/*从字符串读取RSA公钥*/
BIO* enc = NULL;
if ((enc = BIO_new_mem_buf(pubkey, -1)) == NULL)
{
printf("BIO_new_mem_buf failed!\n");
return;
}
/*解析公钥*/
RSA* rsa_pub = RSA_new();
rsa_pub = PEM_read_bio_RSA_PUBKEY(enc, NULL, NULL, NULL);
if (rsa_pub == NULL)
{
printf("Unable to read public key!\n");
return;
}
/*分端循环加密*/
int label = 0, index = 0, index_rest = 0;
int segment = strlen(in_plain) / maxPlain; /*分段数*/
int rest = strlen(in_plain) % maxPlain; /*余数*/
/*明文长度大于最大加密长度且非整数倍*/
if (strlen(in_plain) > maxPlain && rest != 0)
{
for (label = 0; label < segment; label++)
{
memset(plain, '\0', maxPlain);
memset(encrypted, '\0', cipherLen);
memcpy(plain, in_plain + index, maxPlain); /*对明文进行分段*/
plain[maxPlain] = '\0';
int EncryptedLen = RSA_public_encrypt(maxPlain, plain, encrypted, rsa_pub, RSA_PKCS1_PADDING);
if (EncryptedLen == -1)
{
printf("Failed to encrypt!\n");
return;
}
/*对每一段定长密文进行拼接*/
memcpy(result + label * cipherLen, encrypted, cipherLen);
index += maxPlain;
}
/*对剩余部分明文进行加密*/
index_rest = segment * maxPlain;
memset(plain_rest, '\0', rest);
memcpy(plain_rest, in_plain + index_rest, rest); /*获取剩余部分明文*/
plain_rest[rest] = '\0';
memset(encrypted_rest, '\0', cipherLen);
int EncryptedLen = RSA_public_encrypt(rest, plain_rest, encrypted_rest, rsa_pub, RSA_PKCS1_PADDING);
if (EncryptedLen == -1)
{
printf("Failed to encrypt!\n");
return;
}
/*将剩余部分的密文拼接到整段密文中*/
memcpy(result + label * cipherLen, encrypted_rest, cipherLen);
/*对整段密文进行Base64编码*/
Base64Encode(result, (label + 1) * cipherLen, cipher);
}/*多段加密*/
/*明文长度等于最大加密长度的整数倍*/
else if (strlen(in_plain) >= maxPlain && rest == 0)
{
for (label = 0; label < segment; label++)
{
memset(plain, '\0', maxPlain);
memset(encrypted, '\0', cipherLen);
memcpy(plain, in_plain + index, maxPlain); /*对明文进行分段*/
plain[maxPlain] = '\0';
int EncryptedLen = RSA_public_encrypt(maxPlain, plain, encrypted, rsa_pub, RSA_PKCS1_PADDING);
if (EncryptedLen == -1)
{
printf("Failed to encrypt!\n");
return;
}
/*拼接每段密文*/
memcpy(result + label * cipherLen, encrypted, cipherLen);
index += maxPlain;
}
/*对整段密文进行Base64编码*/
Base64Encode(result, label * cipherLen, cipher);
}/*多段整除加密*/
/*明文长度小于最大加密长度*/
else
{
int EncryptedLen = RSA_public_encrypt(strlen(in_plain), in_plain, encrypted, rsa_pub, RSA_PKCS1_PADDING);
if (EncryptedLen == -1)
{
printf("Failed to encrypt!\n");
return;
}
/*对密文进行Base64编码*/
Base64Encode(encrypted, cipherLen, cipher);
}
/*释放BIO内存和RSA结构体*/
BIO_free_all(enc);
RSA_free(rsa_pub);
return;
}
/****************************************************************
* function name : PrivKeyPEMFormat
* functional description : 私钥格式化
* input parameter : privkey:私钥数据内容
* output parameter : None
* return value: None
* history :
*****************************************************************/
void PrivKeyPEMFormat(char* privkey)
{
char format_privkey[MAXLEN] = "";
char priv_tem[MAXLEN] = "";
char* priv_begin = "-----BEGIN RSA PRIVATE KEY-----\n";
char* priv_end = "-----END RSA PRIVATE KEY-----\n";
char* check = strstr(privkey, priv_begin);
if (check)
{
return;
}
else
{
int nPrivateKeyLen = strlen(privkey);
int index = 0, privlength = 0;
memcpy(format_privkey, priv_begin, 32);
for (index = 0; index < nPrivateKeyLen; index += 64)
{
memcpy(priv_tem, privkey + index, 64);
strcat(format_privkey, priv_tem);
privlength = strlen(format_privkey);
format_privkey[privlength] = '\n';
memset(priv_tem, 0, sizeof(priv_tem));
}
strcat(format_privkey, priv_end);
memcpy(privkey, format_privkey, strlen(format_privkey));
}
}
/****************************************************************
* function name : PrivateDecrypt
* functional description : 私钥解密数据
* input parameter : cipher:加密数据;out_plain:解密数据
* output parameter : None
* return value: None
* history :
*****************************************************************/
int PrivateDecrypt(char* cipher, char* out_plain)
{
/*私钥文件内容*/
char private_key[MIDLEN] = "";
read_privatekey_content(private_key);
if (strlen(private_key) == 0)
{
printf("get private key file content error\n");
return;
}
char decode_data[MAXLEN] = ""; /*存放解码后的整段密文*/
char encrypted_result[MAXLEN] = ""; /*存放分段后的每一段密文*/
char decrypted[MAXLEN] = ""; /*存放每一段密文的解密结果*/
/*对私钥进行PEM格式化*/
PrivKeyPEMFormat(private_key);
/*1024位私钥对应的密文长度为128字节*/
int CipherRealLen = 128;
int plainLen = CipherRealLen - 11; /*每段明文的最大长度*/
/*从字符串读取RSA私钥*/
BIO* dec = NULL;
if ((dec = BIO_new_mem_buf(private_key, -1)) == NULL)
{
printf("BIO_new_mem_buf failed!\n");
return;
}
/*解析私钥*/
RSA* rsa_pri = RSA_new();
EVP_PKEY* pri = EVP_PKEY_new();
pri = PEM_read_bio_PrivateKey(dec, NULL, NULL, NULL);
if (pri == NULL)
{
printf("Unable to read private key!\n");
return;
}
/*将EVP_PKEY结构体转换成RSA结构体*/
rsa_pri = EVP_PKEY_get1_RSA(pri);
/*分段循环解密*/
int CipherLen = strlen(cipher); /*Base64编码的密文长度*/
int index = 0, label = 0, out_plainLen = 0;
/*计算真实密文的段数,CipherLen * 3 / 4为密文长度 */
int segment = CipherLen * 3 / 4 / CipherRealLen;
//memset(out_plain, '\0', plainLen);
/*对整段密文进行Base64解码*/
Base64Decode(cipher, CipherLen, decode_data);
/*将解码后的密文分段解密后合并*/
while (label < segment)
{
memset(encrypted_result, '\0', CipherRealLen);
memcpy(encrypted_result, decode_data + index, CipherRealLen); /*对密文进行分段*/
encrypted_result[CipherRealLen] = '\0';
memset(decrypted, '\0', plainLen);
int DecryptedLen = RSA_private_decrypt(CipherRealLen, encrypted_result, decrypted, rsa_pri, RSA_PKCS1_PADDING);
if (DecryptedLen == -1)
{
printf("Failed to decrypt!\n");
return;
}
decrypted[DecryptedLen] = '\0';
strcat(out_plain, decrypted); /*将每一段的解密结果拼接到整段输出明文中*/
out_plainLen += DecryptedLen;
out_plain[out_plainLen] = '\0';
index += CipherRealLen;
label++;
}
/*释放BIO内存以及RSA和EVP_PKEY结构体*/
BIO_free_all(dec);
RSA_free(rsa_pri);
EVP_PKEY_free(pri);
return 0;
}
int main()
{
char input[MAXLEN] = "";
printf("please input your data:\n");
scanf("%s", input);
/*加密数据*/
char encrypt_data[MAXLEN] = "";
PublicEncrypt(input, encrypt_data);
printf("encrypt_data:%s\n", encrypt_data);
/*解密数据*/
char decrypt_data[MAXLEN] = "";
PrivateDecrypt(encrypt_data, decrypt_data);
printf("decrypt_data:%s\n", decrypt_data);
return 0;
}
关注其中几个函数:
/*
*创建一个内存型的BIO,其数据为buf里面len的长度,如果参数len为-1,
*则取的是strlen(buf)的长度。它用于数据需要存储在一块静态内存并以BIO形式存在。
*所需要的数据是直接从内存种读取的,而不是先要执行拷贝操作。
*所以这块内存是只读的。
*/
BIO *BIO_new_mem_buf(void *buf, int len);
/*
*使用openssl库加载rsa的公钥时,使用的函数也不同。
*以字符串公钥为例,对PKCS#1格式的**加载使用PEM_read_bio_RSAPublicKey()函数,
*对PKCS#8格式公钥的加载使用PEM_read_bio_RSA_PUBKEY()函数。
*此处读取的是java生成的公钥,所以使用PEM_read_bio_RSA_PUBKEY
*/
RSA *PEM_read_bio_RSA_PUBKEY(BIO *bp, RSA **x,pem_password_cb *cb, void *u);
/*
*加密数据,
*参数1:被加密的数据长度
*参数2:被加密的数据
*参数3:存放加密后的数据
*参数4:**对
*参数5:填充方式
*/
int RSA_public_encrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
/*
*进行base64编码,openssl的函数,
*参数1:编码后的数据
*参数2:要编码的数据
*参数3:数据长度
*/
int EVP_EncodeBlock(unsigned char *t, const unsigned char *f, int dlen);
/*
*进行base64解码,openssl的函数
*参数1:编码后的数据
*参数2:要解码的数据
*参数3:数据长度
*/
int EVP_DecodeBlock(unsigned char *t, const unsigned char *f, int n);
/*
*从BIO中获取私钥数据,存到EVP_PKEY格式变量中
*/
EVP_PKEY *PEM_read_bio_PrivateKey(BIO *bp, EVP_PKEY **x,
pem_password_cb *cb, void *u);
/*
*解密数据
*参数1:解***长度
*参数2:要解密的数据
*参数3:存放解密后的信息
*参数4:**RSA结构体
*参数5:填充模式
*/
int RSA_private_decrypt(int flen, const unsigned char *from,
unsigned char *to, RSA *rsa, int padding);
openssl相关命令:
1、生成**:
openssl genrsa -out rsa_private_key.pem 1024
-out 指定生成文件,此文件包含公钥和私钥两部分,所以即可以加密,也可以解密
1024 生成**的长度
2、提取公钥
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key_1024.pub
-in 指定输入的**文件
-out 指定提取生成公钥的文件(PEM公钥格式)
3、私钥转换成pkcs8格式
openssl pkcs8 -topk8 -inform PEM -in rsa_private_key.pem -outform PEM -nocrypt > rsa_private_key_pkcs8.pem
参考链接:
下一篇: 计算几何工具算法-求任意多边形的面积