C language RSA algorithm implementation (based on Mbedtls)

C language RSA algorithm implementation (based on Mbedtls)

In recent projects, the RSA algorithm needs to be implemented through the C language. Here I implement it through the Mbedtls library.

1. Download Mbedtls

1. we put the Mbedtls code into the project, the related portals are as follows:

Mbedtls official download address

The official website is slow to download from abroad, so the Mbedtls code used in this article is also attached. The portal is as follows:

Mbedtls encryption and decryption tool code

2. Introduce the Mbedtls header file

Here we introduce the Mbedtls header file in the CMakeLists.txt in the project, the code is as follows:

# for debug
# add_compile_options(-g)

project("device-authentication")

cmake_minimum_required(VERSION 3.5)

INCLUDE_DIRECTORIES(
	../include/
    ../../src/net/mbedtls/include
	../../src/smalgo/sms4/include
)

SET(my_src_crypto
    ../../src/net/mbedtls/library/aes.c
    ../../src/net/mbedtls/library/aesni.c
    ../../src/net/mbedtls/library/base64.c
    ../../src/net/mbedtls/library/rsa.c
    ../../src/net/mbedtls/library/rsa_internal.c
    ../../src/net/mbedtls/library/entropy.c
    ../../src/net/mbedtls/library/entropy_poll.c
    ../../src/net/mbedtls/library/bignum.c
    ../../src/net/mbedtls/library/sha1.c
    ../../src/net/mbedtls/library/sha256.c
    ../../src/net/mbedtls/library/sha512.c
    ../../src/net/mbedtls/library/md.c
    ../../src/net/mbedtls/library/md5.c
    ../../src/net/mbedtls/library/md_wrap.c
    ../../src/net/mbedtls/library/ripemd160.c
    ../../src/net/mbedtls/library/platform_util.c
    ../../src/net/mbedtls/library/oid.c
    ../../src/net/mbedtls/library/timing.c
    ../../src/net/mbedtls/library/net_sockets.c
	../../src/smalgo/sms4/cbc128.c
	../../src/smalgo/sms4/sms4_cbc.c
	../../src/smalgo/sms4/sms4_common.c
	../../src/smalgo/sms4/sms4_enc.c
	../../src/smalgo/sms4/sms4_setkey.c
)
SET(my_src_crypto_dbg
    ../../src/net/mbedtls/library/ctr_drbg.c
)

SET(SRC_LIST_ENCRYPT_BIN
		oem_porting.c
		sdk_porting.c
		authref.c
		test.c
		${my_src_crypto}
		${my_src_crypto_dbg}
		)

SET(SRC_LIST_DECRYPT_LIB 
    oem_porting.c
    sdk_porting.c
    authref.c
    auth.c
    ${my_src_crypto}
    ${my_src_crypto_dbg}
		)

#SET(SRC_LIST_AUTH_DEV
#    oem_porting.c
#    sdk_porting.c
#    authref.c
#    ${my_src_crypto}
#    ${my_src_crypto_dbg}
#)

add_definitions(-fPIC)
#ADD_LIBRARY(authd STATIC ${SRC_LIST_AUTH_DEV})
ADD_LIBRARY(authoal STATIC ${SRC_LIST_DECRYPT_LIB})
ADD_EXECUTABLE(eaidkAuth ${SRC_LIST_ENCRYPT_BIN})
 

The project structure is as follows:

  

After the introduction is complete, we can start RSA code writing.

3. RSA code writing

The authref.h header file code is as follows:

#ifndef __AUTHREF_H__
#define __AUTHREF_H__
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#undef DEBUG

#define RSA_KEY_SIZE     1024        //RSA  
#define EXPONENT         65537
#define BUFFER_SIZE      1024
#define RSA_KEY_LEN      256
#define AES_KEY_DEC_LEN  8
#define LICENSE_DEC_LEN  64

typedef struct __rsa
{
    uint8_t   buf[BUFFER_SIZE*8];
    uint8_t*  rsa_n;
    uint8_t*  rsa_e;
    uint8_t*  rsa_d;
    uint8_t*  rsa_p;
    uint8_t*  rsa_q;
    uint8_t*  rsa_dp;
    uint8_t*  rsa_dq;
    uint8_t*  rsa_qp;
    uint32_t  n_len;
    uint32_t  e_len;
    uint32_t  d_len;
    uint32_t  p_len;
    uint32_t  q_len;
    uint32_t  dp_len;
    uint32_t  dq_len;
    uint32_t  qp_len;
}T_rsa;

void generate_rsa(T_rsa* r);

void init_rsa_keys(T_rsa* rsa);

void rsa_encrypt(  const T_rsa *r, 
	 	const unsigned char* plaintext, 
		unsigned int plaintext_size, 
		unsigned char *ciphertext, 
		size_t *ciphertext_size);

void rsa_decrypt(
	    	const T_rsa *r, 
		const unsigned char* ciphertext, 
		//unsigned int ciphertext_size, 
		unsigned char *plaintext, 
		size_t *plaintext_size);

#ifdef __cplusplus
}
#endif

#endif //__AUTHREF_H__
 

The authref.c code is as follows: 

#include "authref.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/rsa.h"

#define RSA_KEY_SIZE     1024        //RSA  
#define EXPONENT         65537
#define BUFFER_SIZE      1024
#define RSA_KEY_LEN      256
#define AES_KEY_DEC_LEN  8
#define LICENSE_DEC_LEN  64

void generate_rsa(T_rsa* r)
{
    mbedtls_rsa_context    rsa;
    mbedtls_entropy_context    entropy;
    mbedtls_ctr_drbg_context    ctr_drbg;

    mbedtls_entropy_init(&entropy);

    mbedtls_ctr_drbg_init(&ctr_drbg);
    mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);

    mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);

    mbedtls_rsa_gen_key(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg, RSA_KEY_SIZE, EXPONENT);

    mbedtls_mpi_write_binary(&rsa.N, r->rsa_n, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.E, r->rsa_e, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.D, r->rsa_d, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.P, r->rsa_p, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.Q, r->rsa_q, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.DP, r->rsa_dp, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.DQ, r->rsa_dq, BUFFER_SIZE);
    mbedtls_mpi_write_binary(&rsa.QP, r->rsa_qp, BUFFER_SIZE);
    mbedtls_rsa_free(&rsa);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
}

void init_rsa_keys(T_rsa *rsa) {
    memset(rsa->buf, 0, BUFFER_SIZE * 8);
    rsa->rsa_n = &rsa->buf[BUFFER_SIZE * 0];
    rsa->rsa_e = &rsa->buf[BUFFER_SIZE * 1];
    rsa->rsa_d = &rsa->buf[BUFFER_SIZE * 2];
    rsa->rsa_p = &rsa->buf[BUFFER_SIZE * 3];
    rsa->rsa_q = &rsa->buf[BUFFER_SIZE * 4];
    rsa->rsa_dp = &rsa->buf[BUFFER_SIZE * 5];
    rsa->rsa_dq = &rsa->buf[BUFFER_SIZE * 6];
    rsa->rsa_qp = &rsa->buf[BUFFER_SIZE * 7];
    rsa->n_len = BUFFER_SIZE;
    rsa->e_len = BUFFER_SIZE;
    rsa->d_len = BUFFER_SIZE;
    rsa->p_len = BUFFER_SIZE;
    rsa->q_len = BUFFER_SIZE;
    rsa->dp_len = BUFFER_SIZE;
    rsa->dq_len = BUFFER_SIZE;
    rsa->qp_len = BUFFER_SIZE;
}

// 
void rsa_encrypt(
        const T_rsa *r,
        const unsigned char *plaintext,
        unsigned int plaintext_size,
        unsigned char *ciphertext,
        size_t *ciphertext_size) {
    mbedtls_rsa_context rsa;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;
    int rett = 0;

    mbedtls_entropy_init(&entropy);
    //assert(mbedtls_ctr_drbg_init(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0) == 0);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    assert(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) == 0);

    mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);
    assert(mbedtls_mpi_read_binary(&rsa.N, r->rsa_n, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.E, r->rsa_e, BUFFER_SIZE) == 0);

    *ciphertext_size = rsa.len = (mbedtls_mpi_bitlen(&rsa.N) + 7) >> 3;

    //assert(mbedtls_rsa_pkcs1_encrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, plaintext_size, plaintext, ciphertext) == 0);
    rett = mbedtls_rsa_pkcs1_encrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, plaintext_size,
                                     plaintext, ciphertext);
    assert(rett == 0);
    mbedtls_rsa_free(&rsa);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
}

// 
void rsa_decrypt(
        const T_rsa *r,
        const unsigned char *ciphertext,
        //unsigned int ciphertext_size,
        unsigned char *plaintext,
        size_t *plaintext_size) {
    mbedtls_rsa_context rsa;
    mbedtls_entropy_context entropy;
    mbedtls_ctr_drbg_context ctr_drbg;

    mbedtls_entropy_init(&entropy);
    //assert(mbedtls_ctr_drbg_init(&ctr_drbg, mbedtls_entropy_func, &entropy, nullptr, 0) == 0);
    mbedtls_ctr_drbg_init(&ctr_drbg);
    assert(mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0) == 0);

    mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);

    assert(mbedtls_mpi_read_binary(&rsa.N, r->rsa_n, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.E, r->rsa_e, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.D, r->rsa_d, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.P, r->rsa_p, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.Q, r->rsa_q, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.DP, r->rsa_dp, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.DQ, r->rsa_dq, BUFFER_SIZE) == 0);
    assert(mbedtls_mpi_read_binary(&rsa.QP, r->rsa_qp, BUFFER_SIZE) == 0);

    rsa.len = (mbedtls_mpi_bitlen(&rsa.N) + 7) >> 3;

    assert(mbedtls_rsa_pkcs1_decrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, plaintext_size,
                                     ciphertext, plaintext, *plaintext_size) == 0);

    mbedtls_rsa_free(&rsa);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
}

int do_ras_encrypt(authhandle *hdl, uint8_t contract_id[CONTRACTID_LEN], uint8_t user_id[USERID_LEN],
                   uint8_t uid[FINAL_UID_LEN]) {
    T_rsa r;
    uint8_t uid_local[UID_LEN];
    uint8_t ciphertext[SN_LEN];
    size_t ciphertext_len = SN_LEN;
    GenRawUID(hdl, contract_id, user_id, uid_local);
    //encryp UID
    init_rsa_keys(&r);
    if (hdl->callback_func.GetServerPublicKey) {
        hdl->callback_func.GetServerPublicKey(r.buf);
    } else {
        GetServerPublicKey(hdl, r.buf); //load 2048 bytes for public key
    }
    rsa_encrypt(&r, uid_local, UID_LEN, ciphertext, &ciphertext_len);
    //printf("ciphertext_len: %ld\n", ciphertext_len);
    //copy SWID
#if 0
    int i;
    printf("CipherUID: ");
    for(i = 0; i < FINAL_UID_LEN; ++i){
        printf("%02x, ", uid[i]);
    }
    printf("\n");
#endif
    return 0; //OK

}
 

The pseudo code for generating RSA public and private keys is as follows:

T_rsa r;
uint8_t pub_key[2048] = { 0 };
uint8_t prv_key[8192] = { 0 };

init_rsa_keys(&r);
generate_rsa(&r);

//1. public key
memcpy(pub_key, r.buf, 2048);

//2. private key
memcpy(prv_key, r.buf, 8192);
 

The RSA encryption code is as follows:

int do_ras_encrypt(authhandle *hdl, uint8_t contract_id[CONTRACTID_LEN], uint8_t user_id[USERID_LEN],
                   uint8_t uid[FINAL_UID_LEN]) {
    T_rsa r;
    uint8_t uid_local[UID_LEN];
    uint8_t ciphertext[SN_LEN];
    size_t ciphertext_len = SN_LEN;
    //encryp UID
    init_rsa_keys(&r);
    if (hdl->callback_func.GetServerPublicKey) {
        hdl->callback_func.GetServerPublicKey(r.buf);
    } else {
        GetServerPublicKey(hdl, r.buf); //load 2048 bytes for public key
    }
    rsa_encrypt(&r, uid_local, UID_LEN, ciphertext, &ciphertext_len);
    //printf("ciphertext_len: %ld\n", ciphertext_len);
    //copy SWID
#if 0
    int i;
    printf("CipherUID: ");
    for(i = 0; i < FINAL_UID_LEN; ++i){
        printf("%02x, ", uid[i]);
    }
    printf("\n");
#endif
    return 0; //OK

}
 

The decryption code is as follows:

static int SetServerPrivateKey(T_rsa *r, uint8_t *prv_key)
{
    memcpy(r->buf, prv_key, 8192);
    //memset(&r->buf[0], 0, 1024-128);
    //memset(&r->buf[1024], 0, 1024-4);
    //memset(&r->buf[2048], 0, 1024-128);
    //memset(&r->buf[3072], 0, 1024-64);
    //memset(&r->buf[4096], 0, 1024-64);
    //memset(&r->buf[5120], 0, 1024-64);
    //memset(&r->buf[6144], 0, 1024-64);
    //memset(&r->buf[7168], 0, 1024-64);
    return 0; //OK
}

JNIEXPORT jbyteArray JNICALL Java_com_openailab_oascloud_security_jni_cloud_CloudAuthJNI_decrypUidOnServer
        (JNIEnv *env, jobject obj, jstring uidEnc, jbyteArray privateKey)
{
    uint8_t *uid_enc = (*env)->GetStringUTFChars(env, uidEnc, NULL);
    uint8_t *private_key = (*env)->GetByteArrayElements(env, privateKey, NULL);

    jbyteArray ret = NULL;
    T_rsa r;
    uint8_t plaintext[UID_LEN];
    size_t plaintext_len = 256;
    uint8_t UID_Recover[SN_LEN];
    uint8_t uid_enc_c[FINAL_UID_LEN];

    init_rsa_keys(&r);
    SetServerPrivateKey(&r, private_key);

    hex2array(uid_enc, FINAL_UID_LEN * 2, uid_enc_c);

    recover(&uid_enc_c[FINAL_UID_LEN - SN_LEN], UID_Recover, SN_LEN);
    rsa_decrypt(&r, UID_Recover, plaintext, &plaintext_len);

    /*printf("plaintext=\n");
    for (size_t i = 0; i < UID_LEN; i++)
    {
    if (i > 0 && i % 16 == 0) {
    printf("\n");
    }
    printf("%02x,", plaintext[i]);
    }
    printf("\n");*/

    ret = (*env)->NewByteArray(env, UID_LEN);
    (*env)->SetByteArrayRegion(env, ret, 0, UID_LEN, (jbyte*)plaintext);
    (*env)->ReleaseByteArrayElements(env, privateKey, private_key, 0);
    (*env)->ReleaseStringUTFChars(env, uidEnc, uid_enc);
    return ret;
}
 

At this point, the introduction of C language RSA encryption and decryption is complete.