MacOS下使用C语言基于openssl库进行RSA加密解密
1 安装openssl并生成密钥
首先当然要安装openssl(这里记得看一下安装路径,应该是/usr/local/Cellar/openssl@3
之类的):
brew install openssl
安装完了以后执行:
cd /usr/local/include
ln -s ../opt/openssl/include/openssl .
创建项目,生成公钥私钥:
openssl genrsa -out rsa_private_key.pem 1024
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem
2 编写RSA加密解密代码
编写test.c
文件:
// RSA 加密 ///
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <stdbool.h>
#define PATH_TO_PRIVATE_KEY "rsa_private_key.pem"
#define PATH_TO_PUBLIC_KEY "rsa_public_key.pem"
#define BUFFSIZE 1024
char *my_encrypt(char *str, char *path_key); //加密
char *my_decrypt(char *str, char *path_key); //解密
int main(void)
{
char *original_text = "I hate coding!";
char *ciphertext, *plaintext;
printf("original_text is :%s\n", original_text);
//1.加密
ciphertext = my_encrypt(original_text, PATH_TO_PUBLIC_KEY);
printf("ciphertext is :%s\n", ciphertext);
//2.解密
plaintext = my_decrypt(ciphertext, PATH_TO_PRIVATE_KEY);
printf("plaintext is :%s\n", plaintext);
if(ciphertext)
free(ciphertext);
if(plaintext)
free(plaintext);
return 0;
}
//加密
char *my_encrypt(char *str, char *path_key)
{
char *p_en = NULL;
RSA *p_rsa = NULL;
FILE *file = NULL;
int rsa_len = 0; //flen为源文件长度, rsa_len为秘钥长度
// printf("文件名:%s\n", path_key);
//1.打开秘钥文件
if((file = fopen(path_key, "rb")) == NULL)
{
perror("fopen() rsa_public_key error \n ");
goto End;
}
//2.从公钥中获取 加密的秘钥
if((p_rsa = PEM_read_RSA_PUBKEY(file, NULL,NULL,NULL )) == NULL)
{
ERR_print_errors_fp(stdout);
goto End;
}
//3.获取秘钥的长度
rsa_len = RSA_size(p_rsa);
//4.为加密后的内容 申请空间(根据秘钥的长度+1)
p_en = (char *)malloc(rsa_len + 1);
if(!p_en)
{
perror("malloc() error\n");
goto End;
}
memset(p_en, 0, rsa_len + 1);
//5.对内容进行加密
if(RSA_public_encrypt(rsa_len, (unsigned char*)str, (unsigned char*)p_en, p_rsa, RSA_NO_PADDING) < 0)
{
perror("RSA_public_encrypt() error\n");
goto End;
}
End:
//6.释放秘钥空间, 关闭文件
if(p_rsa) RSA_free(p_rsa);
if(file) fclose(file);
return p_en;
}
//解密
char *my_decrypt(char *str, char *path_key)
{
char *p_de = NULL;
RSA *p_rsa = NULL;
FILE *file = NULL;
int rsa_len = 0;
// printf("文件名:%s\n", path_key);
//1.打开秘钥文件
file = fopen(path_key, "rb");
if(!file)
{
perror("fopen() rsa_private_key error \n ");
goto End;
}
//2.从私钥中获取 解密的秘钥
if((p_rsa = PEM_read_RSAPrivateKey(file, NULL,NULL,NULL )) == NULL)
{
ERR_print_errors_fp(stdout);
goto End;
}
//3.获取秘钥的长度,
rsa_len = RSA_size(p_rsa);
//4.为加密后的内容 申请空间(根据秘钥的长度+1)
p_de = (char *)malloc(rsa_len + 1);
if(!p_de)
{
perror("malloc() error \n");
goto End;
}
memset(p_de, 0, rsa_len + 1);
//5.对内容进行加密
if(RSA_private_decrypt(rsa_len, (unsigned char*)str, (unsigned char*)p_de, p_rsa, RSA_NO_PADDING) < 0)
{
perror("RSA_public_encrypt() error \n");
goto End;
}
End:
//6.释放秘钥空间, 关闭文件
if(p_rsa) RSA_free(p_rsa);
if(file) fclose(file);
return p_de;
}
编写makefile
文件:
CC = gcc
CFLAGS = -Wall -g
LDFLAGS =
SRC_DIR = ./src
INC_DIR = ./include
OBJ_DIR = ./obj
SRC = $(wildcard *.c) $(wildcard $(SRC_DIR)/*.c)
INC = $(wildcard *.h) $(wildcard $(INC_DIR)/*.h)
INCLUDE = -I$(INC_DIR)
#DIR = $(notdir$(SRC))
OBJ = $(addprefix $(OBJ_DIR)/,$(notdir $(patsubst %.c,%.o,$(SRC))))
# 寻找文件的顺序
VPATH = $(SRC_DIR):$(INC_DIR)
TARGET = test
all: $(TARGET)
$(TARGET):$(OBJ)
$(CC) $^ -o $@
$(OBJ_DIR)/%.o:$(SRC)
mkdir -p $(OBJ_DIR)
$(CC) $(INCLUDE) -c $(CFLAGS) $< -o $@
clean:
rm -rf $(OBJ_DIR)
rm -f $(TARGET)
目录结构:
执行make命令,报错如下:
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [test] Error 1
出现这个错误的原因是我们没有把openssl库链接过来,需要修改makefile
文件:
CC = gcc
CFLAGS = -Wall -g
LDFLAGS =
LIBS = -lssl -lcrypto
LIBPATH = -L /usr/local/Cellar/openssl@3/3.0.0_1/lib
SRC_DIR = ./src
INC_DIR = ./include
OBJ_DIR = ./obj
SRC = $(wildcard *.c) $(wildcard $(SRC_DIR)/*.c)
INC = $(wildcard *.h) $(wildcard $(INC_DIR)/*.h)
INCLUDE = -I$(INC_DIR)
#DIR = $(notdir$(SRC))
OBJ = $(addprefix $(OBJ_DIR)/,$(notdir $(patsubst %.c,%.o,$(SRC))))
# 寻找文件的顺序
VPATH = $(SRC_DIR):$(INC_DIR)
TARGET = test
all: $(TARGET)
$(TARGET):$(OBJ)
**$(CC) $^ -o $@ $(LIBPATH) $(LIBS)**
$(OBJ_DIR)/%.o:$(SRC)
mkdir -p $(OBJ_DIR)
$(CC) $(INCLUDE) -c $(CFLAGS) $< -o $@
clean:
rm -rf $(OBJ_DIR)
rm -f $(TARGET)
重新编译,可以通过了。程序运行结果如下:
$ ./test
original_text is :I hate coding!
ciphertext is :<�?"��h~�
�}oPeQ�Vh��s�4��W��"s�0+�L�o�T��n�w���A�+��~��?k6�5�
plaintext is :I hate coding!
3 Base64 编解码
接下来,为程序增加base64编解码函数(这里我在网上找的老哥代码里面decode的时候设置了不换行,encode的时候却没有设置不换行,坑死我了):
int base64_encode(char *in_str, int in_len, char *out_str)
{
BIO *b64, *bio;
BUF_MEM *bptr = NULL;
int size = 0;
if (in_str == NULL || out_str == NULL)
return -1;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);//不换行!
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_write(bio, in_str, in_len);
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bptr);
memcpy(out_str, bptr->data, bptr->length);
out_str[bptr->length] = '\0';
size = bptr->length;
BIO_free_all(bio);
return size;
}
int base64_decode(char *in_str, int in_len, char *out_str) {
BIO *b64, *bio;
// BUF_MEM *bptr = NULL;
// int counts;
int size = 0;
if (in_str == NULL || out_str == NULL)
return -1;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);//不换行!
bio = BIO_new_mem_buf(in_str, in_len);
bio = BIO_push(b64, bio);
size = BIO_read(bio, out_str, in_len);
out_str[size] = '\0';
BIO_free_all(bio);
return size;
}
修改main
函数:
int main(void)
{
char *original_text = "I hate coding!";
char *ciphertext, *plaintext;
printf("original_text is :%s\n", original_text);
//1.加密
ciphertext = my_encrypt(original_text, PATH_TO_PUBLIC_KEY);
printf("ciphertext is :%s\n", ciphertext);
//2.base64编码
int length = strlen(ciphertext);
char* str_after_encode = (char*)malloc(1024);
base64_encode(ciphertext, length, str_after_encode);
printf("base64编码结果: %s\n", str_after_encode);
//3.base64解码
int length2 = strlen(str_after_encode);
char* str_after_decode = (char*)malloc(1024);
base64_decode(str_after_encode, length2, str_after_decode);
printf("base64解码结果: %s\n", str_after_decode);
//4.解密
plaintext = my_decrypt(str_after_decode, PATH_TO_PRIVATE_KEY);
printf("plaintext is :%s\n", plaintext);
if(ciphertext)
free(ciphertext);
if(plaintext)
free(plaintext);
if(str_after_encode)
free(str_after_encode);
if(str_after_decode)
free(str_after_decode);
return 0;
}
运行结果:
$ ./test
original_text is :I hate coding!
ciphertext is :Q���R�kq�H�&
$5i�[�nS���L+�i���0� w+��$�����:
�R�]/�!>nDZS2p��=�9���:<�5`��т�`F��ï������k
$��;]5����(sF3�����U%3
base64编码结果: UZuO5FLva3GrSIwmDBokNWm2W9ZuU9nE4EwrD8VprNjyMOoOCXcWK83sJMGkrImROgoc4FLwHF0vtCE+bsexUx0ycO29up097Ls5jufjrjo8rDVgk+fRgogQYEaUHMzDr5rp+6P972sMJKmRO101g7+ish8oc0Yzttb3qMBVJTM=
base64解码结果: Q���R�kq�H�&
$5i�[�nS���L+�i���0� w+��$�����:
�R�]/�!>nDZS2p��=�9���:<�5`��т�`F��ï������k
$��;]5����(sF3�����U%3
plaintext is :I hate coding!
4 移植到Linux
系统信息:
$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description: Ubuntu 18.04.4 LTS
Release: 18.04
Codename: bionic
首先,我们从源码编译一下openssl:
wget https://www.openssl.org/source/old/3.0/openssl-3.0.0.tar.gz
tar -zvxf openssl-3.0.0.tar.gz
cd openssl-3.0.0/
./config -fPIC no-shared
make
编译完了以后应该能在当前目录下看见libssl.a
和libcrypto.a
文件:
在我们之前的项目文件夹下,新建一个lib文件夹,并将库文件拷贝进来,此外还要拷贝一下openssl用到的头文件:
mkdir lib
cd [path to openssl-3.0.0]
cp libssl.a libcrypto.a [path to your project/lib]
cp -r ./inlucde/openssl [path to your project/lib]
回到项目文件夹,修改makefile
文件:
CC = gcc
CFLAGS = -Wall -g
LDFLAGS =
LIBS = -lssl -lcrypto -lpthread -ldl
LIBPATH = -L ./lib
SRC_DIR = ./src
INC_DIR = ./include
OBJ_DIR = ./obj
SRC = $(wildcard *.c) $(wildcard $(SRC_DIR)/*.c)
INC = $(wildcard *.h) $(wildcard $(INC_DIR)/*.h)
INCLUDE = -I$(INC_DIR)
#DIR = $(notdir$(SRC))
OBJ = $(addprefix $(OBJ_DIR)/,$(notdir $(patsubst %.c,%.o,$(SRC))))
# 寻找文件的顺序
VPATH = $(SRC_DIR):$(INC_DIR)
TARGET = test
all: $(TARGET)
$(TARGET):$(OBJ)
$(CC) $^ -o $@ $(LIBPATH) $(LIBS)
$(OBJ_DIR)/%.o:$(SRC)
mkdir -p $(OBJ_DIR)
$(CC) $(INCLUDE) -c $(CFLAGS) $< -o $@
clean:
rm -rf $(OBJ_DIR)
rm -f $(TARGET)
这里一定要注意,链接库的顺序千万不能乱动,因为链接的时候是有着严格的依赖顺序的,在链接库时函数是向后查找的,具体的排序应该是调用库,被调用库,被被调用库。
看一下现在的项目结构:
$ tree -L 2
.
├── include
│ └── openssl
├── lib
│ ├── libcrypto.a
│ └── libssl.a
├── makefile
├── rsa_private_key.pem
├── rsa_public_key.pem
└── src
└── test.c
现在编译应该已经可以成功了,程序输出结果也是正确的:
$ ./test
original_text is :I hate coding!
ciphertext is :Q���R�kq�H�&
$5i�[�nS���L+�i���0� w+��$�����:
�R�]/�!>nDZS2p����=�9���:<�5`��т�`F��ï�����k
$��;]5����(sF3����U%3
base64编码结果: UZuO5FLva3GrSIwmDBokNWm2W9ZuU9nE4EwrD8VprNjyMOoOCXcWK83sJMGkrImROgoc4FLwHF0vtCE+bsexUx0ycO29up097Ls5jufjrjo8rDVgk+fRgogQYEaUHMzDr5rp+6P972sMJKmRO101g7+ish8oc0Yzttb3qMBVJTM=
base64解码结果: Q���R�kq�H�&
$5i�[�nS���L+�i���0� w+��$�����:
�R�]/�!>nDZS2p����=�9���:<�5`��т�`F��ï�����k
$��;]5����(sF3����U%3
plaintext is :I hate coding!
参考文章
openssl C语言编码实现rsa加密 - 路之遥_其漫漫 - 博客园
ld: library not found for -lgsl
Base64编码、解码 C语言例子(使用OpenSSL库)_Leon-CSDN博客_base64库 c语言