RSA encryption and decryption using C language based on openssl Library under MacOS

RSA encryption and decryption using C language based on openssl Library under MacOS

1 install openssl and generate the key

First of all, of course, you should install OpenSSL (remember to look at the installation path here, which should be / usr / local / cell)/ openssl@3 Like):

brew install openssl

After installation:

cd /usr/local/include
ln -s ../opt/openssl/include/openssl .

Create project and generate public and private keys:

openssl genrsa -out rsa_private_key.pem 1024
openssl rsa -in rsa_private_key.pem -pubout -out rsa_public_key.pem

2 write RSA encryption and decryption code

Write test C) documents:

//  RSA encryption///
#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);    //encryption
char *my_decrypt(char *str, char *path_key);    //decrypt

int main(void)
{
  char *original_text = "I hate coding!";

  char *ciphertext, *plaintext;

  printf("original_text is :%s\n", original_text);

  //1. Encryption
  ciphertext = my_encrypt(original_text, PATH_TO_PUBLIC_KEY);
  printf("ciphertext is :%s\n", ciphertext);

  //2. Decryption
  plaintext = my_decrypt(ciphertext, PATH_TO_PRIVATE_KEY);
  printf("plaintext is :%s\n", plaintext);

  if(ciphertext)
    free(ciphertext);
  if(plaintext)
    free(plaintext);
  return 0;
}

//encryption
char *my_encrypt(char *str, char *path_key)
{
  char *p_en = NULL;
  RSA  *p_rsa = NULL;
  FILE *file = NULL;

  int  rsa_len = 0;    //flen is the length of the source file, rsa_len is the length of the secret key
//  printf("file name:% s\n", path_key);
  //1. Open the secret key file
  if((file = fopen(path_key, "rb")) == NULL)
  {
    perror("fopen() rsa_public_key error \n ");
    goto End;
  }

  //2. Obtain the encrypted secret key from the public key
  if((p_rsa = PEM_read_RSA_PUBKEY(file, NULL,NULL,NULL )) == NULL)
  {
    ERR_print_errors_fp(stdout);
    goto End;
  }

  //3. Length of secret key
  rsa_len = RSA_size(p_rsa);

  //4. Apply for space for the encrypted content (according to the length of the secret key + 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. Encrypt the content
  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. Release the secret key space and close the file
  if(p_rsa)    RSA_free(p_rsa);
  if(file)     fclose(file);

  return p_en;
}

//decrypt
char *my_decrypt(char *str, char *path_key)
{
  char *p_de = NULL;
  RSA  *p_rsa = NULL;
  FILE *file = NULL;
  int   rsa_len = 0;

//  printf("file name:% s\n", path_key);
  //1. Open the secret key file
  file = fopen(path_key, "rb");
  if(!file)
  {
    perror("fopen() rsa_private_key error \n ");
    goto End;
  }

  //2. Obtain the decrypted secret key from the private key
  if((p_rsa = PEM_read_RSAPrivateKey(file, NULL,NULL,NULL )) == NULL)
  {
    ERR_print_errors_fp(stdout);
    goto End;
  }

  //3. The length of the secret key obtained,
  rsa_len = RSA_size(p_rsa);

  //4. Apply for space for the encrypted content (according to the length of the secret key + 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. Encrypt the content
  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. Release the secret key space and close the file
  if(p_rsa)    RSA_free(p_rsa);
  if(file)     fclose(file);

  return p_de;
}

Write makefile file:

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))))

# Order of finding files
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)

Directory structure:

Execute the make command and an error is reported as follows:

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

The reason for this error is that we did not link the openssl library. We need to modify the makefile file:

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))))

# Order of finding files
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)

Recompile, you can pass. The running results of the program are as follows:

$ ./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 codec

Next, add the base64 codec function to the program (here, I set no line break in the old brother's code I found online, but I didn't set no line break in encode, which killed me):

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);//No line breaks!
  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);//No line breaks!

  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;
}

Modify the main function:

int main(void)
{
  char *original_text = "I hate coding!";

  char *ciphertext, *plaintext;

  printf("original_text is :%s\n", original_text);

  //1. Encryption
  ciphertext = my_encrypt(original_text, PATH_TO_PUBLIC_KEY);
  printf("ciphertext is :%s\n", ciphertext);

  //2.base64 coding
  int length = strlen(ciphertext);
  char* str_after_encode = (char*)malloc(1024);
  base64_encode(ciphertext, length, str_after_encode);
  printf("base64 Coding results: %s\n", str_after_encode);

  //3.base64 decoding
  int length2 = strlen(str_after_encode);
  char* str_after_decode = (char*)malloc(1024);
  base64_decode(str_after_encode, length2, str_after_decode);
  printf("base64 Decoding result: %s\n", str_after_decode);

  //4. Decryption
  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;

}

Operation results:

$ ./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 Coding results: UZuO5FLva3GrSIwmDBokNWm2W9ZuU9nE4EwrD8VprNjyMOoOCXcWK83sJMGkrImROgoc4FLwHF0vtCE+bsexUx0ycO29up097Ls5jufjrjo8rDVgk+fRgogQYEaUHMzDr5rp+6P972sMJKmRO101g7+ish8oc0Yzttb3qMBVJTM=
base64 Decoding result: 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 porting to Linux

System information:

$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.4 LTS
Release:	18.04
Codename:	bionic

First, let's compile openssl from the source code:

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

After compiling, you should be able to see libssl in the current directory A and libcrypto A documents:

In our previous project folder, create a new lib folder and copy the library file. In addition, copy the header file used by 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]

Go back to the project folder and modify the makefile file:

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))))

# Order of finding files
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)

It must be noted here that the order of the link library must not be disorderly, because there is a strict dependency order when linking. When linking the library, the functions are searched backward. The specific order should be the calling library, the called Library and the called library.

Take a look at the current project structure:

$ tree -L 2
.
├── include
│   └── openssl
├── lib
│   ├── libcrypto.a
│   └── libssl.a
├── makefile
├── rsa_private_key.pem
├── rsa_public_key.pem
└── src
    └── test.c

Now the compilation should be successful, and the program output result is correct:

$ ./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 Coding results: UZuO5FLva3GrSIwmDBokNWm2W9ZuU9nE4EwrD8VprNjyMOoOCXcWK83sJMGkrImROgoc4FLwHF0vtCE+bsexUx0ycO29up097Ls5jufjrjo8rDVgk+fRgogQYEaUHMzDr5rp+6P972sMJKmRO101g7+ish8oc0Yzttb3qMBVJTM=
base64 Decoding result: 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!

Reference articles

openssl C language coding to achieve rsa encryption - road away_ Its long - blog Garden

Compilation and use of OpenSSL static library (linux Environment)

ld: library not found for -lgsl

RSA encryption (3.0)

Encryption algorithm and file format RSA, X509, PKCSXX?

base64 encoding and decoding c language examples (using OpenSSL Library)_ Leon CSDN blog_ base64 Library C language

Order of libraries when linked

Keywords: C OpenSSL rsa

Added by shapiro125 on Sun, 09 Jan 2022 10:45:43 +0200