RSA加密算法是一种非对称加密算法。在公开密钥加密和电子商业中RSA被广泛使用。本文介绍如何使用openssl命令和C代码实现基础的RSA加/解密和签名/验签功能。
一、openssl命令实现RSA加解密
1、生成私钥和公钥
- 生成私钥
openssl genrsa -out private.key 2048 #生成私钥
- 使用私钥生成公钥
openssl rsa -pubout -in private.key -out public.key #私钥生成公钥
2、签名、验签
- 使用private.key私钥签名data.txt,生成data.sign
openssl rsautl -sign -inkey private.key -keyform PEM -in data.txt -out data.sign #签名
- 使用公钥验签verify.key
openssl rsautl -verify -inkey public.key -pubin -in data.sign #验签
注意:执行生成密钥对和验签命令时,一定要注意文件名后面是否有空格符或tab符,否则会出现"No such file or directory"的报错
可以看到验签后,打印信息与签名前的文件内容相同,签名、验签测试OK。
3、加密、解密
- 使用公钥加密 data.txt 生成 data.encrypt
openssl rsautl -encrypt -pubin -inkey public.key -in data.txt -out data.encrypt
注意:每次用公钥加密生成的加密文件内容都会变化
- 使用私钥解密 data.encrypt
openssl rsautl -decrypt -inkey private.key -in data.encrypt
如果要存到文件,解密命令后面加 "-out data.decrypt"
4、其他openssl命令
- 从certificate.crt证书提取公钥到public_key.pem
openssl x509 -pubkey -noout -in certificate.crt > public_key.pem
- 直接使用证书验签(从证书获取公钥,使用公钥验签)
openssl rsautl -verify -inkey certificate.crt -certin -in data.sign
二、C语言实现RSA非对称加解密
下面使用的密钥对openssl命令生成的 private.key 和 public.key
1、签名、验签
1.1 C代码实现
代码下载链接:https://download.csdn.net/download/hinewcc/89484338
- openssl_rsa.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include "openssl_rsa.h"
void rsa_init(void)
{
printf("rsa init ... \n");
OpenSSL_add_all_algorithms();
}
/*
*****************************************************************************************
* 函 数 名: rsa_sign
* 功能说明: RSA使用私钥签名
* 形 参: key : 私钥
* file_in : 输入需要签名的文件
* file_out : 签名后生成的文件
* 返 回 值: 0:成功, 其他:失败
*****************************************************************************************
*/
int rsa_sign(const char *key, const char *file_in, const char *file_out)
{
int ret = -1;
int keysize = 0;
RSA *rsa = NULL;
BIO *in = NULL, *out = NULL;
unsigned char *rsa_in = NULL, *rsa_out = NULL;
int rsa_inlen = 0, rsa_outlen = 0, len = 0;
/* 从私钥提取 RSA */
FILE *fp = fopen(key, "r");
if (NULL == fp) {
perror("Open Key Error: \n");
return -1;
}
rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL,NULL);
if (!rsa) {
perror("EVP_PKEY_get1_RSA Error: \n");
goto error;
}
/* 打开file_in文件内容,创建file_out文件 */
in = BIO_new_file(file_in, "rb");
if (NULL == in) {
printf("BIO_new_file: open source file fail\n");
goto error;
}
out = BIO_new_file(file_out, "wb");
if (NULL == out) {
printf("BIO_new_file: open file_out file fail\n");
goto error;
}
keysize = RSA_size(rsa);
if (keysize < 0) {
printf("RSA_size key size is %d\n", keysize);
goto error;
}
rsa_in =(unsigned char *)OPENSSL_malloc(keysize * 2);
if (rsa_in == NULL){
perror("OPENSSL_malloc ras in fail\n");
goto error;
}
rsa_out =(unsigned char *)OPENSSL_malloc(keysize);
if (rsa_out == NULL){
perror("OPENSSL_malloc ras out fail\n");
goto error;
}
rsa_inlen = BIO_read(in, rsa_in, keysize * 2);
if (rsa_inlen < 0) {
perror("BIO_read Fail: \n");
goto error;
}
/* 使用私钥验签,写入file_out文件 */
len = RSA_private_encrypt(rsa_inlen, rsa_in, rsa_out, rsa, RSA_PKCS1_PADDING);
if (len < 0) {
perror("RSA_encrypt Fail: \n");
goto error;
}
rsa_outlen = BIO_write(out, rsa_out, len);
if (rsa_outlen < 0) {
perror("BIO_write Fail: \n");
goto error;
}
ret = 0;
printf("sign success!\n");
error:
if (NULL != fp) fclose(fp);
if (rsa) RSA_free(rsa);
if (in) BIO_free(in);
if (out) BIO_free_all(out);
if (rsa_in) OPENSSL_free(rsa_in);
if (rsa_out) OPENSSL_free(rsa_out);
return ret;
}
/*
*****************************************************************************************
* 函 数 名: rsa_verify
* 功能说明: RSA使用公钥验签
* 形 参: key : 私钥
* file_in : 输入需要签名的文件
* file_out : 签名后生成的文件
* 返 回 值: 0:成功, 其他:失败
*****************************************************************************************
*/
int rsa_verify(const char *public, const char *file_in,const char *file_out)
{
int ret = ERR_NONE;
int keysize = 0;
RSA* rsa = NULL;
BIO* in = NULL, *out = NULL;
unsigned char* rsa_in = NULL, *rsa_out = NULL;
int rsa_inlen = 0, rsa_outlen = 0, len = 0;
/* 从公钥提取 RSA */
FILE *fp = fopen(public, "rb");
if (NULL == fp) {
perror("Open Key Error: \n");
return -1;
}
if(NULL == (rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL,NULL)))
{
printf( "PEM_read_RSAPrivateKey error\n");
fclose(fp);
goto error;
}
/* 打开file_in文件内容,创建file_out文件 */
in = BIO_new_file(file_in, "rb");
if (NULL == in) {
printf("BIO_new_file: open file_in file fail\n");
ret = ERR_KEY_EN_OPEN;
goto error;
}
out = BIO_new_file(file_out, "wb");
if (NULL == out) {
printf("BIO_new_file: open file_out file fail\n");
ret = ERR_KEY_DE_OPEN;
goto error;
}
keysize = RSA_size(rsa);
if (keysize < 0) {
printf("RSA_size key size is %d\n", keysize);
ret = ERR_RSA_SIZE;
goto error;
}
rsa_in = (unsigned char *)OPENSSL_malloc(keysize * 2);
if (rsa_in == NULL){
perror("OPENSSL_malloc ras in fail\n");
ret = ERR_RSAIN_MALLOC;
goto error;
}
rsa_out = (unsigned char *)OPENSSL_malloc(keysize);
if (rsa_out == NULL) {
perror("OPENSSL_malloc ras out fail\n");
ret = ERR_RSAOUT_MALLOC;
goto error;
}
// Read ENCYRPTED_FILE
rsa_inlen = BIO_read(in, rsa_in, keysize * 2);
if (rsa_inlen < 0) {
perror("BIO_read Fail: \n");
ret = ERR_KEY_EN_R;
goto error;
}
/* 使用公钥验签,写入file_out文件 */
len = RSA_public_decrypt(rsa_inlen, rsa_in, rsa_out, rsa, RSA_PKCS1_PADDING);
if (len < 0) {
perror("Decrypt Fail: \n");
ret = ERR_KEY_DE;
goto error;
}
// Write DECRYPTED_FILE
rsa_outlen = BIO_write(out, rsa_out, len);
if (rsa_outlen < 0) {
perror("BIO_write Fail: \n");
ret = ERR_KEY_DE_W;
goto error;
}
printf("verify success!\n");
error:
if (NULL != fp) fclose(fp);
if (rsa) RSA_free(rsa);
if (in) BIO_free(in);
if (out) BIO_free_all(out);
if (rsa_in) OPENSSL_free(rsa_in);
if (rsa_out) OPENSSL_free(rsa_out);
return ret;
}
- openssl_rsa.h
#ifndef _OPENSSL_RSA_H_
#define _OPENSSL_RSA_H_
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define ERR_NONE 0
// DECRYPT
#define ERR_EMMC_KEY_OPEN 0x01
#define ERR_EMMC_KEY_WRITE 0x02
#define ERR_EMMC_KEY_SIZE 0x03
#define ERR_FILE_KEY_OPEN 0x03
#define ERR_FILE_KEY_READ 0x04
#define ERR_KEY_EN_OPEN 0x05
#define ERR_KEY_DE_OPEN 0x06
#define ERR_RSA_SIZE 0x07
#define ERR_RSAIN_MALLOC 0x08
#define ERR_RSAOUT_MALLOC 0x09
#define ERR_KEY_DE 0x0A
#define ERR_KEY_EN_R 0x0B
#define ERR_KEY_DE_W 0x0C
#define ERR_AES_TK 0x0D
#define ERR_FILE_EN_OPEN 0x0E
#define ERR_FILE_DE_INIT 0x0F
#define ERR_FILE_DE_UPDATE 0x10
#define ERR_FILE_DE_OPEN 0x11
#define ERR_FILE_DE_W 0x12
#define ERR_FILE_DE_FINAL 0x13
void rsa_init(void);
int rsa_sign(const char *key, const char *file_in, const char *file_out);
int rsa_verify(const char *public, const char *file_in,const char *file_out);
#endif
- main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "openssl_rsa.h"
int main(int argc, char **argv)
{
int i;
char acOpt[96] = {0};
char acKey[96] = {0};
char acFile_in[96] = {0};
char acFile_Out[96] = {0};
if (argc != 5) {
printf("usage: ./app -sign|-verify key in out");
return -1;
}
strcpy(acKey, argv[2]);
strcpy(acFile_in, argv[3]);
strcpy(acFile_Out, argv[4]);
printf("key: %s\n", acKey);
printf("input file: %s\n", acFile_in);
printf("output file: %s\n", acFile_Out);
if (strcmp(argv[1], "-sign") == 0) {
rsa_sign(acKey, acFile_in, acFile_Out);
} else if (strcmp(argv[1], "-verify") == 0) {
rsa_verify(acKey, acFile_in, acFile_Out);
} else {
printf("usage: ./app -sign|-verify key in out");
return -1;
}
return 0;
}
1.2 编译
$ gcc -o rsa_test main.c openssl_rsa.c -lcrypto
编译生成可执行程序 rsa_test
1.3 测试验证
$ ./rsa_test -sign private.key file.txt file.sign #签名
使用private.key私钥对file.txt文件签名,生成file.sign
$ ./rsa_test -verify public.key file.sign out.txt #验签
使用public.key公钥对file.sign签名文件验签,将输出的文件打印出来,内容与签名前的file.txt相同,验证OK。
2、加密、解密
2.1 C代码实现
代码下载链接:https://download.csdn.net/download/hinewcc/89484462
加密、解密 与 签名、验签 的代码基本相同,差异地方如下:
1)公钥能实现验签/加密功能,加密、验签调用的函数不同:
- RSA_public_decrypt:用公钥解密(验签)
- RSA_public_encrypt:用公钥加密
2)私钥能实现签名/解密功能,签名、解密调用的函数不同:
- RSA_private_encrypt:用私钥加密(签名)
- RSA_private_decrypt:用私钥解密
openssl_rsa.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <error.h>
#include "openssl_rsa.h"
void rsa_init(void)
{
printf("rsa init ... \n");
OpenSSL_add_all_algorithms();
}
/*
*****************************************************************************************
* 函 数 名: rsa_decrypt
* 功能说明: RSA使用私钥解密
* 形 参: key : 私钥
* file_in : 输入需要签名的文件
* file_out : 签名后生成的文件
* 返 回 值: 0:成功, 其他:失败
*****************************************************************************************
*/
int rsa_decrypt(const char *key, const char *file_in, const char *file_out)
{
int ret = -1;
int keysize = 0;
RSA *rsa = NULL;
BIO *in = NULL, *out = NULL;
unsigned char *rsa_in = NULL, *rsa_out = NULL;
int rsa_inlen = 0, rsa_outlen = 0, len = 0;
/* 从私钥提取 RSA */
FILE *fp = fopen(key, "r");
if (NULL == fp) {
perror("Open Key Error: \n");
return -1;
}
rsa = PEM_read_RSAPrivateKey(fp, NULL, NULL,NULL);
if (!rsa) {
perror("EVP_PKEY_get1_RSA Error: \n");
goto error;
}
/* 打开file_in文件内容,创建file_out文件 */
in = BIO_new_file(file_in, "rb");
if (NULL == in) {
printf("BIO_new_file: open source file fail\n");
goto error;
}
out = BIO_new_file(file_out, "wb");
if (NULL == out) {
printf("BIO_new_file: open file_out file fail\n");
goto error;
}
keysize = RSA_size(rsa);
if (keysize < 0) {
printf("RSA_size key size is %d\n", keysize);
goto error;
}
rsa_in =(unsigned char *)OPENSSL_malloc(keysize * 2);
if (rsa_in == NULL){
perror("OPENSSL_malloc ras in fail\n");
goto error;
}
rsa_out =(unsigned char *)OPENSSL_malloc(keysize);
if (rsa_out == NULL){
perror("OPENSSL_malloc ras out fail\n");
goto error;
}
rsa_inlen = BIO_read(in, rsa_in, keysize * 2);
if (rsa_inlen < 0) {
perror("BIO_read Fail: \n");
goto error;
}
/* 使用私钥解密,写入file_out文件 */
len = RSA_private_decrypt(rsa_inlen, rsa_in, rsa_out, rsa, RSA_PKCS1_PADDING);
if (len < 0) {
perror("RSA_encrypt Fail: \n");
goto error;
}
rsa_outlen = BIO_write(out, rsa_out, len);
if (rsa_outlen < 0) {
perror("BIO_write Fail: \n");
goto error;
}
ret = 0;
printf("sign success!\n");
error:
if (NULL != fp) fclose(fp);
if (rsa) RSA_free(rsa);
if (in) BIO_free(in);
if (out) BIO_free_all(out);
if (rsa_in) OPENSSL_free(rsa_in);
if (rsa_out) OPENSSL_free(rsa_out);
return ret;
}
/*
*****************************************************************************************
* 函 数 名: rsa_encrypt
* 功能说明: RSA使用公钥加密
* 形 参: key : 公钥
* file_in : 输入需要签名的文件
* file_out : 签名后生成的文件
* 返 回 值: 0:成功, 其他:失败
*****************************************************************************************
*/
int rsa_encrypt(const char *public, const char *file_in,const char *file_out)
{
int ret = ERR_NONE;
int keysize = 0;
RSA* rsa = NULL;
BIO* in = NULL, *out = NULL;
unsigned char* rsa_in = NULL, *rsa_out = NULL;
int rsa_inlen = 0, rsa_outlen = 0, len = 0;
/* 从公钥提取 RSA */
FILE *fp = fopen(public, "rb");
if (NULL == fp) {
perror("Open Key Error: \n");
return -1;
}
if(NULL == (rsa = PEM_read_RSA_PUBKEY(fp, NULL, NULL,NULL)))
{
printf( "PEM_read_RSAPrivateKey error\n");
fclose(fp);
goto error;
}
/* 打开file_in文件内容,创建file_out文件 */
in = BIO_new_file(file_in, "rb");
if (NULL == in) {
printf("BIO_new_file: open file_in file fail\n");
ret = ERR_KEY_EN_OPEN;
goto error;
}
out = BIO_new_file(file_out, "wb");
if (NULL == out) {
printf("BIO_new_file: open file_out file fail\n");
ret = ERR_KEY_DE_OPEN;
goto error;
}
keysize = RSA_size(rsa);
if (keysize < 0) {
printf("RSA_size key size is %d\n", keysize);
ret = ERR_RSA_SIZE;
goto error;
}
rsa_in = (unsigned char *)OPENSSL_malloc(keysize * 2);
if (rsa_in == NULL){
perror("OPENSSL_malloc ras in fail\n");
ret = ERR_RSAIN_MALLOC;
goto error;
}
rsa_out = (unsigned char *)OPENSSL_malloc(keysize);
if (rsa_out == NULL) {
perror("OPENSSL_malloc ras out fail\n");
ret = ERR_RSAOUT_MALLOC;
goto error;
}
// Read ENCYRPTED_FILE
rsa_inlen = BIO_read(in, rsa_in, keysize * 2);
if (rsa_inlen < 0) {
perror("BIO_read Fail: \n");
ret = ERR_KEY_EN_R;
goto error;
}
/* 使用公钥加密,写入file_out文件 */
len = RSA_public_encrypt(rsa_inlen, rsa_in, rsa_out, rsa, RSA_PKCS1_PADDING);
if (len < 0) {
perror("Decrypt Fail: \n");
ret = ERR_KEY_DE;
goto error;
}
// Write DECRYPTED_FILE
rsa_outlen = BIO_write(out, rsa_out, len);
if (rsa_outlen < 0) {
perror("BIO_write Fail: \n");
ret = ERR_KEY_DE_W;
goto error;
}
printf("verify success!\n");
error:
if (NULL != fp) fclose(fp);
if (rsa) RSA_free(rsa);
if (in) BIO_free(in);
if (out) BIO_free_all(out);
if (rsa_in) OPENSSL_free(rsa_in);
if (rsa_out) OPENSSL_free(rsa_out);
return ret;
}
openssl_rsa.h
#ifndef _OPENSSL_RSA_H_
#define _OPENSSL_RSA_H_
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#define ERR_NONE 0
// DECRYPT
#define ERR_EMMC_KEY_OPEN 0x01
#define ERR_EMMC_KEY_WRITE 0x02
#define ERR_EMMC_KEY_SIZE 0x03
#define ERR_FILE_KEY_OPEN 0x03
#define ERR_FILE_KEY_READ 0x04
#define ERR_KEY_EN_OPEN 0x05
#define ERR_KEY_DE_OPEN 0x06
#define ERR_RSA_SIZE 0x07
#define ERR_RSAIN_MALLOC 0x08
#define ERR_RSAOUT_MALLOC 0x09
#define ERR_KEY_DE 0x0A
#define ERR_KEY_EN_R 0x0B
#define ERR_KEY_DE_W 0x0C
#define ERR_AES_TK 0x0D
#define ERR_FILE_EN_OPEN 0x0E
#define ERR_FILE_DE_INIT 0x0F
#define ERR_FILE_DE_UPDATE 0x10
#define ERR_FILE_DE_OPEN 0x11
#define ERR_FILE_DE_W 0x12
#define ERR_FILE_DE_FINAL 0x13
void rsa_init(void);
int rsa_decrypt(const char *key, const char *file_in, const char *file_out);
int rsa_encrypt(const char *public, const char *file_in,const char *file_out);
#endif
main.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "openssl_rsa.h"
int main(int argc, char **argv)
{
int i;
char acOpt[96] = {0};
char acKey[96] = {0};
char acFile_in[96] = {0};
char acFile_Out[96] = {0};
if (argc != 5) {
printf("usage: ./app -encrypt|-decrypt key in out");
return -1;
}
strcpy(acKey, argv[2]);
strcpy(acFile_in, argv[3]);
strcpy(acFile_Out, argv[4]);
printf("key: %s\n", acKey);
printf("input file: %s\n", acFile_in);
printf("output file: %s\n", acFile_Out);
if (strcmp(argv[1], "-encrypt") == 0) { //加密
rsa_encrypt(acKey, acFile_in, acFile_Out);
} else if (strcmp(argv[1], "-decrypt") == 0) {
rsa_decrypt(acKey, acFile_in, acFile_Out);
} else {
printf("usage: ./app -encrypt|-decrypt key in out");
return -1;
}
printf("success!!!\n");
return 0;
}
2.2 编译
$ gcc -o rsa_test main.c openssl_rsa.c -lcrypto
2.3 测试过程
同上,不再赘述