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