MacOS下使用C语言基于openssl库进行RSA加密解密
1 安装openssl并生成密钥
首先当然要安装openssl(这里记得看一下安装路径,应该是/usr/local/Cellar/openssl@3
之类的):
brew install openssl
安装完了以后执行:
cd /usr/local/includeln -s ../opt/openssl/include/openssl .
创建项目,生成公钥私钥:
openssl genrsa -out rsa_private_key.pem 1024openssl 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 1024char *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 = gccCFLAGS = -Wall -gLDFLAGS =SRC_DIR = ./srcINC_DIR = ./includeOBJ_DIR = ./objSRC = $(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 = testall: $(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_64clang: error: linker command failed with exit code 1 (use -v to see invocation)make: *** [test] Error 1
出现这个错误的原因是我们没有把openssl库链接过来,需要修改makefile
文件:
CC = gccCFLAGS = -Wall -gLDFLAGS =LIBS = -lssl -lcryptoLIBPATH = -L /usr/local/Cellar/openssl@3/3.0.0_1/libSRC_DIR = ./srcINC_DIR = ./includeOBJ_DIR = ./objSRC = $(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 = testall: $(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)
重新编译,可以通过了。程序运行结果如下:
$ ./testoriginal_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;}
运行结果:
$ ./testoriginal_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%3base64编码结果: 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%3plaintext is :I hate coding!
4 移植到Linux
系统信息:
$ lsb_release -aNo LSB modules are available.Distributor ID:UbuntuDescription:Ubuntu 18.04.4 LTSRelease:18.04Codename:bionic
首先,我们从源码编译一下openssl:
wget /source/old/3.0/openssl-3.0.0.tar.gztar -zvxf openssl-3.0.0.tar.gzcd openssl-3.0.0/./config -fPIC no-sharedmake
编译完了以后应该能在当前目录下看见libssl.a
和libcrypto.a
文件:
在我们之前的项目文件夹下,新建一个lib文件夹,并将库文件拷贝进来,此外还要拷贝一下openssl用到的头文件:
mkdir libcd [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 = gccCFLAGS = -Wall -gLDFLAGS =LIBS = -lssl -lcrypto -lpthread -ldlLIBPATH = -L ./libSRC_DIR = ./srcINC_DIR = ./includeOBJ_DIR = ./objSRC = $(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 = testall: $(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
现在编译应该已经可以成功了,程序输出结果也是正确的:
$ ./testoriginal_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%3base64编码结果: 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%3plaintext is :I hate coding!
参考文章
openssl C语言编码实现rsa加密 - 路之遥_其漫漫 - 博客园
OpenSSL静态库编译及使用(linux环境)
ld: library not found for -lgsl
RSA加密(3.0)
加密算法和文件格式RSA、X509、PKCSXX?
Base64编码、解码 C语言例子(使用OpenSSL库)_Leon-CSDN博客_base64库 c语言
链接时库的顺序