最近有个项目需要用到rsa加密和解密,平台是cortex-m4单片机,于是我想用炒的比较火的mbedtls库实现。既然这玩意炒的比较火,资料应该不少才对,上手以后才发现资料少的可怜,仅有的资料基本都是在注水,参考意义不大,pem格式的秘钥操作这一块基本相当于没有资料,只能自己硬着头皮搞了。搞了两天以后终于搞出来了。
关于pem相关知识这里不做介绍,我就直接贴代码。
#if !defined(MBEDTLS_CONFIG_FILE)
#include "mbedtls/config.h"
#else
#include MBEDTLS_CONFIG_FILE
#endif
#if defined(MBEDTLS_PLATFORM_C)
#include "mbedtls/platform.h"
#else
#include <stdio.h>
#include <stdlib.h>
#define mbedtls_printf printf
#define mbedtls_exit exit
#define MBEDTLS_EXIT_SUCCESS EXIT_SUCCESS
#define MBEDTLS_EXIT_FAILURE EXIT_FAILURE
#endif /* MBEDTLS_PLATFORM_C */
#include "mbedtls/rsa.h"
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/pk.h"
#include <stdio.h>
#include <string.h>
int rsa_encrypt()
{
FILE* f = NULL;
long size;
size_t n;
unsigned char* publickey = NULL;
mbedtls_pk_context ctx_pk;
/*********************************************/
int ret = 1;
size_t i;
mbedtls_rsa_context rsa;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
unsigned char input[1024];
unsigned char buf[512];
const char* pers = "rsa_encrypt";
mbedtls_mpi N, E; //定义一个大数,也就是公钥
/*****************************************************/
mbedtls_pk_init(&ctx_pk);
if ((f = fopen("rsa4096_pub.pem", "rb")) == NULL) //打开pem格式的公钥文件
{
mbedtls_printf("\n . Open public key file failed!");
return(-1);
}
fseek(f, 0, SEEK_END);
if ((size = ftell(f)) == -1)
{
fclose(f);
return(-1);
}
fseek(f, 0, SEEK_SET);
n = (size_t)size; //
if (n + 1 == 0 || (publickey = mbedtls_calloc(1, n + 1)) == NULL)
{
fclose(f);
return(-1);
}
if (fread(publickey, 1, n, f) != n)
{
fclose(f);
free(publickey);
publickey = NULL;
return(-1);
}
fclose(f);
/*从pem文件里获得公钥*/
if (0 != mbedtls_pk_parse_public_key(&ctx_pk, publickey, n + 1))
{
mbedtls_printf("\n . Can't import public key");
}
else
{
mbedtls_printf("\n . Import public key successfully");
}
free(publickey);
publickey = NULL;
/*****************************************************************/
mbedtls_printf("\n . Seeding the random number generator...");
memset(input, 0, 1024);
fflush(stdout);
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&E);
mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);
mbedtls_ctr_drbg_init(&ctr_drbg); //初始化ctr drbg结构体,用于随机数的生成
mbedtls_entropy_init(&entropy); //初始化熵源
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char*)pers, strlen(pers)); //生成随机数
if (ret != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
goto exit;
}
/*导入pem内的公钥*/
rsa = *(mbedtls_rsa_context*)ctx_pk.pk_ctx;
input[0] = 'H';
input[1] = 'E';
input[2] = 'L';
input[3] = 'L';
input[4] = 'O';
input[5] = ',';
input[6] = 'W';
input[7] = 'O';
input[8] = 'R';
input[9] = 'L';
input[10] = 'D';
input[11] = '!';
input[12] = '\0';
/*
* Calculate the RSA encryption of the hash.
*/
mbedtls_printf("\n . Generating the RSA encrypted value");
fflush(stdout);
/*加密操作,利用公钥加密*/
ret = mbedtls_rsa_pkcs1_encrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PUBLIC, 12, input, buf);
if (ret != 0)
{
mbedtls_printf(" failed\n ! mbedtls_rsa_pkcs1_encrypt returned %d\n\n", ret);
goto exit;
}
/*
* Write the signature into result-enc.txt
*/
if ((f = fopen("result-enc.txt", "wb+")) == NULL) //将加密文件写入到result-enc.txt
{
mbedtls_printf(" failed\n ! Could not create %s\n\n", "result-enc.txt");
goto exit;
}
for (i = 0; i < rsa.len; i++)
{
mbedtls_fprintf(f, "%02X%s", buf[i], (i + 1) % 16 == 0 ? "\r\n" : " ");
}
fclose(f);
mbedtls_printf("\n . Done (created \"%s\")\n\n", "result-enc.txt");
exit:
/*释放资源*/
mbedtls_mpi_free(&N);
mbedtls_mpi_free(&E);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
mbedtls_rsa_free(&rsa);
return 0;
}
int rsa_decrypt()
{
FILE* f = NULL;
long size;
size_t n;
unsigned char* privatekey = NULL;
mbedtls_pk_context ctx_pk;
/*******************************/
int ret = 1;
int c;
size_t i;
mbedtls_rsa_context rsa;
mbedtls_mpi N, P, Q, D, E, DP, DQ, QP; //定义大数
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
unsigned char result[1024];
unsigned char buf[512];
const char* pers = "rsa_decrypt";
memset(result, 0, sizeof(result));
/*********************************/
mbedtls_pk_init(&ctx_pk);
if ((f = fopen("rsa4096_prv.pem", "rb")) == NULL) //打开pem格式的公钥文件
{
mbedtls_printf("\n . Open private key file failed!");
return(-1);
}
fseek(f, 0, SEEK_END);
if ((size = ftell(f)) == -1)
{
fclose(f);
return(-1);
}
fseek(f, 0, SEEK_SET);
n = (size_t)size; //
if (n + 1 == 0 || (privatekey = mbedtls_calloc(1, n + 1)) == NULL)
{
fclose(f);
return(-1);
}
if (fread(privatekey, 1, n, f) != n)
{
fclose(f);
free(privatekey);
privatekey = NULL;
return(-1);
}
fclose(f);
/*从pem文件里获得私钥*/
if (0 != mbedtls_pk_parse_key(&ctx_pk, privatekey, n + 1, NULL, 0))
{
mbedtls_printf("\n . Can't import private key");
}
else
{
mbedtls_printf("\n . Import private key successfully");
}
free(privatekey);
privatekey = NULL;
mbedtls_printf("\n . Seeding the random number generator...");
fflush(stdout);
mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V15, 0);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
mbedtls_mpi_init(&N);
mbedtls_mpi_init(&P);
mbedtls_mpi_init(&Q);
mbedtls_mpi_init(&D);
mbedtls_mpi_init(&E);
mbedtls_mpi_init(&DP);
mbedtls_mpi_init(&DQ);
mbedtls_mpi_init(&QP);
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func,
&entropy, (const unsigned char*)pers,
strlen(pers));
if (ret != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n",
ret);
goto exit;
}
/*导入pem内的私钥*/
rsa = *(mbedtls_rsa_context*)ctx_pk.pk_ctx;
if ((ret = mbedtls_rsa_complete(&rsa)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_rsa_complete returned %d\n\n", ret);
goto exit;
}
/*
* Extract the RSA encrypted value from the text file
*/
if ((f = fopen("result-enc.txt", "rb")) == NULL)
{
mbedtls_printf("\n ! Could not open %s\n\n", "result-enc.txt");
goto exit;
}
i = 0;
while (fscanf(f, "%02X", &c) > 0 && i < (int)sizeof(buf))
buf[i++] = (unsigned char)c;
fclose(f);
if (i != rsa.len)
{
mbedtls_printf("\n ! Invalid RSA signature format\n\n");
goto exit;
}
/*
* Decrypt the encrypted RSA data and print the result.
*/
mbedtls_printf("\n . Decrypting the encrypted data");
fflush(stdout);
ret = mbedtls_rsa_pkcs1_decrypt(&rsa, mbedtls_ctr_drbg_random, &ctr_drbg, MBEDTLS_RSA_PRIVATE, &i, buf, result, 1024);
if (ret != 0)
{
mbedtls_printf(" failed\n ! mbedtls_rsa_pkcs1_decrypt returned %d\n\n", ret);
goto exit;
}
mbedtls_printf("\n . OK\n\n");
mbedtls_printf("The decrypted result is: '%s'\n\n", result);
exit:
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
mbedtls_rsa_free(&rsa);
mbedtls_mpi_free(&N);
mbedtls_mpi_free(&P);
mbedtls_mpi_free(&Q);
mbedtls_mpi_free(&D);
mbedtls_mpi_free(&E);
mbedtls_mpi_free(&DP);
mbedtls_mpi_free(&DQ);
mbedtls_mpi_free(&QP);
return 0;
}
int main()
{
int exit_code = 0;
rsa_encrypt(); //加密
rsa_decrypt(); //解密
#if defined(_WIN32)
mbedtls_printf(" + Press Enter to exit this program.\n");
fflush(stdout);
getchar();
#endif
return(exit_code);
}
mbedtls库:https://github.com/ARMmbed/mbedtls
开发环境:visual studio 2019
ps:我使用的公钥和私钥的在tests/data_files内,也可以使用自己生成的
2021.02.03补充宏定义
/* System support */
#define MBEDTLS_PLATFORM_C //启用平台抽象接口,重新定义printf,sprintf 和malloc,free
#define MBEDTLS_PLATFORM_MEMORY //启用内存分配接口,需要自己实现malloc,free
//#define MBEDTLS_MEMORY_BUFFER_ALLOC_C //启用mbedtls自带的内存分配接口,适用于没有动态内存分配的平台
#define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS //不使用标准的库函数,如malloc,free
//#define MBEDTLS_PLATFORM_EXIT_ALT //允许使用平台exit接口
#define MBEDTLS_NO_PLATFORM_ENTROPY //不使用内置熵源,需要自己实现熵 ,不定义该宏,使用内置熵,只有window和linux才可以注释掉,其他平台不支持内置熵
#define MBEDTLS_NO_DEFAULT_ENTROPY_SOURCES //取消默认熵,不定义该宏,就会使用默认熵
#define MBEDTLS_PLATFORM_PRINTF_ALT //使能printf替换
#define MBEDTLS_PLATFORM_SNPRINTF_ALT //使能sprintf替换
#define MBEDTLS_AES_C //AES
#define MBEDTLS_SHA256_C //SHA256
#define MBEDTLS_ENTROPY_C //使能熵源
#define MBEDTLS_CTR_DRBG_C //随机数模块
#define MBEDTLS_BIGNUM_C //大数模块
//#define MBEDTLS_GENPRIME //使能素数生成,如果生成RSA的公钥和私钥需要定义此宏
#define MBEDTLS_MD_C //使能MD通用接口,计算消息摘要
#define MBEDTLS_OID_C //必须定义
#define MBEDTLS_RSA_C //RSA
#define MBEDTLS_PKCS1_V15 //RSA填充方案
//#define MBEDTLS_RSA_NO_CRT //是否通过中国剩余定理加速
#define MBEDTLS_AES_ROM_TABLES //预定义s盒,可节约部分内存空间
#define MBEDTLS_ERROR_C //错误代码解析
#define MBEDTLS_BASE64_C //使能Base64编码
//#define MBEDTLS_SHA256_SMALLER
#define MBEDTLS_FS_IO //txt格式的秘钥文件
/*pem格式秘钥需要定义的宏*/
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PK_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_PEM_PARSE_C