MacOS下使用C语言基于openssl库进行RSA加密解密

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)

目录结构:
MacOS下使用C语言基于openssl库进行RSA加密解密

执行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.alibcrypto.a文件:

MacOS下使用C语言基于openssl库进行RSA加密解密

在我们之前的项目文件夹下,新建一个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加密 - 路之遥_其漫漫 - 博客园

OpenSSL静态库编译及使用(linux环境)

ld: library not found for -lgsl

RSA加密(3.0)

加密算法和文件格式RSA、X509、PKCSXX?

Base64编码、解码 C语言例子(使用OpenSSL库)_Leon-CSDN博客_base64库 c语言

链接时库的顺序

上一篇:CentOS7.9升级OpenSSH7.4到OpenSSL-8.8p1总结


下一篇:ARM汇编程序调试