mbedtls移植之RSA签名验签算法(数字签名)

本文介绍了MbedTLSSSL库在嵌入式系统中的应用,包括RSA算法的概述,PKCS#V1.5和V2.1填充方式,以及如何使用MbedTLS进行公钥私钥的签名和验签,提供了代码示例和运行效果分析。

一、 mbedtls简介

MbedTLS是一个开源、可移植、易使用、可读性高的SSL库,实现了常所用的加解密算法、X.509证书操作以及TLS协议操作。MbedTLS各功能模块独立性高、耦合度低,可以通过配置宏定义进行功能裁剪,非常适合对空间和效率要求高的嵌入式系统。

二、RSA算法简介

1978年,由Ron Rivest、Adi Shamir和Reonard Adleman共同发表了公钥密码算法RSA,RSA目前是使用广泛的非对称加解密和签名验签算法。RSA密钥由公钥和私钥组成,基本特性如下:
1.公钥和私钥是成对出现的,一个公钥必然对应一个固定的私钥。同理,一个私钥也必然对应一个固定的公钥;
2.在加解密缓解,公钥用于加密,私钥用于解密;
3.在签名验签环节,私钥用于签名,公钥用于验签;
4.公钥通常是公开的,任何人都可以获取到,但私钥必须严格保密;
5.RSA按分组进行,若分组长度不足(例如1024bits或2048bits),则需要填充。填充方式分为PKCS#V1.5和PKCS#V2.1
6.非对称算法性能远低于对称算法

三、签名验签简介

签名验签机制被称为数字签名,通过该机制可实现完整性(Integrity)、认证性(Authenticity)、不和否认性(Non-Repudiation)的保护。通过使用私钥对原始数据进行签名,并同步将签名信息传递给接收者。接受者收到后,使用对应的公钥对签名进行验签,以确认接收到的原始数据是否有效。
原则为:私钥签名,公钥验签
详细请见RFC3447
总体流程如下图:
在这里插入图片描述

四、实现

demo代码:https://download.youkuaiyun.com/download/anjiyufei/88863376

4.1 移植MbedTLS代码

移植自mbedTLS 2.16版本
需移植的文件如下:
在这里插入图片描述
在这里插入图片描述

修改config.h文件

#ifndef MBEDTLS_CONFIG_H
#define MBEDTLS_CONFIG_H

#define MBEDTLS_ERROR_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_OID_C
#define MBEDTLS_RSA_C
#define MBEDTLS_AES_C
#define MBEDTLS_MD_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_GENPRIME
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_PK_C
#define MBEDTLS_SHA256_C


//读pem证书
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_FS_IO

//填充方式,V15或V21两种模式必须二选一
//#define MBEDTLS_PKCS1_V21
#define MBEDTLS_PKCS1_V15

//不使用平台默认熵源,mbedtls在windows和linux下已实现熵源
#define MBEDTLS_NO_PLATFORM_ENTROPY
//#include "check_config.h"

#endif

4.2 引入头文件

引入相应的头文件

#include <stdio.h>
#include <time.h>
#include <string.h>
#include "../crypto/mbedtls/rsa.h"
#include "../crypto/mbedtls/ctr_drbg.h"
#include "../crypto/mbedtls/entropy.h"
#include "../crypto/mbedtls/entropy_poll.h"
#include "../crypto/mbedtls/sha256.h"
#include "../crypto/mbedtls/pk.h"
#include "../crypto/mbedtls/error.h"

4.3 填充方式

RSA加解密时存在两种填充方式:PKCS#V1.5、PKCS#V2.1,必须在config.h中指定填充方式。
mbedtls的RSA中默认使用PKCS#V1.5的填充方式,若在代码中使用PKCS#V2.1,代码中需额外设置,具体设置见后续代码备注。
两种填充方式的差异此处不展开介绍。

4.3.1 PKCS#V2.1说明

在使用PK库时,mbedtls已提示默认使用PKCS#V1.5,无接口去调用PKCS#V2.1的填充方式
在这里插入图片描述

但实际测试发现,可通过mbedtls_rsa_set_padding(private_rsa_context,MBEDTLS_RSA_PKCS_V21,MBEDTLS_MD_SHA256)去实现PKCS#V2.1的填充
当添加如上代码后,跟踪到mbedtls_rsa_pkcs1_sign函数,可发现ctx的padding为1
在这里插入图片描述
在这里插入图片描述

4.4 公私钥和熵源

详见之前博客《mbedtls移植之RSA加解密算法》的3.4章节,此处不额外说明

4.5 读取密钥

从文件中读取pem格式的公私钥

//获取公钥
int get_public_key_from_file( mbedtls_pk_context *public_pk_context,const char *public_pem_file){
    char error[100];
    int result = mbedtls_pk_parse_public_keyfile(public_pk_context,public_pem_file);
    if(result != 0){
        printf("failed to parse public key from file:");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        return -1;
    }else{
        printf("succeeded to parse public key from file!\n");
    }
    return 0;
}

//获取私钥
int get_private_key_from_file(mbedtls_pk_context *private_pk_context,const char *private_pem_file){

    char error[100];
    int result = mbedtls_pk_parse_keyfile(private_pk_context,private_pem_file,NULL);
    if(result != 0){
        printf("failed to parse private key from file:");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        return -1;
    }else{
        printf("succeeded to parse private key from file!\n");
    }
    return 0;
}

4.6 计算HASH

在对文件进行签名时,需先计算文件的HASH,再使用RSA私钥进行签名。使用SHA256计算HASH过程如下:

int get_hash_from_file_by_sha256(const char *sign_file,unsigned char *hash){
    FILE *input_file = fopen(sign_file,"rb");
    unsigned char input_buffer[1024];
    size_t read_num = 0;
    mbedtls_sha256_context sha256_context;

    mbedtls_sha256_init(&sha256_context);
    mbedtls_sha256_starts(&sha256_context,0);
    while(1){
        read_num = fread(input_buffer,sizeof(char ),1024,input_file);
        if( read_num > 0){
            if(read_num == 1024){
                mbedtls_sha256_update(&sha256_context,input_buffer,read_num);
            }else{
                //文件尾
                mbedtls_sha256_update(&sha256_context,input_buffer,read_num);
                break;
            }
        }else{
            printf("failed to read file\n");
            mbedtls_sha256_free(&sha256_context);
            fclose(input_file);
            return -1;
        }
    }

    mbedtls_sha256_finish(&sha256_context,hash);
    /*
    printf("sha256 info:\n");
    for(int i=0;i<32;i++){
        printf("%02x",hash[i]);
    }
    printf("\n");
			*/
    fclose(input_file);
    mbedtls_sha256_free(&sha256_context);
    return 0;
}

4.7 签名和验签

有两种方式可以实现签名验签
1.使用RSA库
2.使用PK库(PK库为对RSA库的封装,包括封装了ECC算法,本质上最终还是会调用到RSA库或ECC库)
推荐使用PK库

4.7.1 调用RSA库

1.读取私钥和公钥信息
2.对待签名文件使用SHA256算法得到文件的HASH值
3.签名:使用私钥对文件HASH值进行签名,得到签名值。签名使用函数mbedtls_rsa_pkcs1_sign
4.验签:使用对应的公钥对签名值进行验证,得到验签结果。验签使用函数mbedtls_rsa_pkcs1_verify

注意:
默认使用PKCS#V1.5填充算法,若需使用PKCS#V2.1填充算法,请参考代码中注释部分

int sign_verify_by_rsa(){
    //个性化初始值:用于初始化伪随机数生成器,可设置为任意值
    const char *personalization = "Fr789jj-ikrkjfjs@";
    mbedtls_pk_context public_pk_context;
    mbedtls_pk_context private_pk_context;
    const char *public_pem_file = "D:\\tmp\\crypto\\rsa\\public.pem";
    const char *private_pem_file = "D:\\tmp\\crypto\\rsa\\private.pem";
    const char *sign_verify_file = "D:\\tmp\\crypto\\rsa\\music.mp3";
    char error[100];
    unsigned char signature_info[256];
    unsigned char hash_buffer[32];

    mbedtls_entropy_context entropy_context;
    mbedtls_ctr_drbg_context ctr_drbg_context;

    mbedtls_entropy_init(&entropy_context);
    mbedtls_ctr_drbg_init(&ctr_drbg_context);
    mbedtls_pk_init(&public_pk_context);
    mbedtls_pk_init(&private_pk_context);

    mbedtls_entropy_add_source(&entropy_context,get_clock_for_entropy,NULL,MBEDTLS_ENTROPY_MIN_PLATFORM,MBEDTLS_ENTROPY_SOURCE_STRONG);
    int result = mbedtls_ctr_drbg_seed(&ctr_drbg_context, mbedtls_entropy_func, &entropy_context, personalization,strlen(personalization));
    if(result){
        printf("failed to get drbg seed\n");
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -1;
    }

    result = get_private_key_from_file(&private_pk_context,private_pem_file);
    if(result != 0){
        printf("failed to get private key!\n");
        return -2;
    }

    //对文件进行签名

    //计算待加密文件HASH
    result = get_hash_from_file_by_sha256(sign_verify_file,hash_buffer);
    if(result != 0){
        printf("failed to get hash from file\n");
    }
    mbedtls_rsa_context *private_rsa_context = mbedtls_pk_rsa(private_pk_context);
    //因PK默认填充方式为V15,若需要使用V21填充方式,需添加如下代码。若使用V15,则无需添加。若使用V21填充方式,需同步在config.h中启用#define MBEDTLS_PKCS1_V21
    //mbedtls_rsa_set_padding(private_rsa_context,MBEDTLS_RSA_PKCS_V21,MBEDTLS_MD_SHA256);
    result = mbedtls_rsa_pkcs1_sign(private_rsa_context,mbedtls_ctr_drbg_random,&ctr_drbg_context,MBEDTLS_RSA_PRIVATE,MBEDTLS_MD_SHA256,sizeof(hash_buffer),hash_buffer,signature_info);
    if(result != 0){
        printf("failed to signature:");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        return -3;
    }else{
        printf("succeeded to signature!\n");
        printf("signature info:\n");
        for(int i=0;i<256;i++){
            printf("%02x",signature_info[i]);
        }
        printf("\n");
        //签名写入文件
        FILE *sign_file = fopen("D:\\tmp\\crypto\\rsa\\mbedtls_sign-rsa.bin","wb");
        int write_num = fwrite(signature_info,sizeof(char),256,sign_file);
        if(write_num != 256){
            printf("failed to write signature to file\n");
        }
        fclose(sign_file);
    }

    //修改签名信息,查看验签是否失败
    //memset(signature_info+255,0xFF,1);

    //验签
    //从文件中读取公钥
    result = get_public_key_from_file(&public_pk_context,public_pem_file);
    if(result != 0){
        printf("failed to get public key!\n");
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -4;
    }
    //计算待加密文件HASH
    result = get_hash_from_file_by_sha256(sign_verify_file,hash_buffer);
    if(result != 0){
        printf("failed to get hash from file\n");
    }
    mbedtls_rsa_context *public_rsa_context = mbedtls_pk_rsa(public_pk_context);
    //因PK默认填充方式为V15,若需要使用V21填充方式,需添加如下代码。若使用V15,则无需添加。若使用V21填充方式,需同步在config.h中启用#define MBEDTLS_PKCS1_V21
    //mbedtls_rsa_set_padding(public_rsa_context,MBEDTLS_RSA_PKCS_V21,MBEDTLS_MD_SHA256);
    result = mbedtls_rsa_pkcs1_verify(public_rsa_context,mbedtls_ctr_drbg_random,&ctr_drbg_context,MBEDTLS_RSA_PUBLIC,MBEDTLS_MD_SHA256,sizeof(hash_buffer),hash_buffer,signature_info);
    if(result == 0){
        printf("succeeded to verify signature!\n");
    }else{
        printf("failed to verify signature:");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -5;
    }

    free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
    return 0;

}
4.7.1.1 运行效果

1.正常运行效果
在这里插入图片描述

2.篡改签名信息后验证
代码中启用注释掉的代码memset(signature_info+255,0xFF,1);
最终提示验签失败
在这里插入图片描述

3.使用openssl验签
上述代码中,成功后的签名信息会保存到D:\tmp\crypto\rsa\mbedtls_sign-rsa.bin
使用openssl命令进行验签,也可以验签通过(openssl使用PKCS#V1.5填充进行验签。若使用PKCS#V2.1签名,因填充方式不一致,会导致验签失败)
在这里插入图片描述
在这里插入图片描述

4.7.2 调用PK库

1.读取私钥和公钥信息
2.对待签名文件使用SHA256算法得到文件的HASH值
3.签名:使用私钥对文件HASH值进行签名,得到签名值。签名使用函数mbedtls_pk_sign
4.验签:使用对应的公钥对签名值进行验证,得到验签结果。验签使用函数mbedtls_pk_verify

注意:
默认使用PKCS#V1.5填充算法,若需使用PKCS#V2.1填充算法,请参考代码中注释部分

int sign_verify_by_pk(){
    //个性化初始值:用于初始化伪随机数生成器,可设置为任意值
    const char *personalization = "Fr789jj-ikrkjfjs@";
    mbedtls_pk_context public_pk_context;
    mbedtls_pk_context private_pk_context;
    const char *public_pem_file = "D:\\tmp\\crypto\\rsa\\public.pem";
    const char *private_pem_file = "D:\\tmp\\crypto\\rsa\\private.pem";
    const char *sign_verify_file = "D:\\tmp\\crypto\\rsa\\music.mp3";
    char error[100];
    unsigned char signature_info[256];
    unsigned char hash_buffer[32];

    mbedtls_entropy_context entropy_context;
    mbedtls_ctr_drbg_context ctr_drbg_context;

    mbedtls_entropy_init(&entropy_context);
    mbedtls_ctr_drbg_init(&ctr_drbg_context);
    mbedtls_pk_init(&public_pk_context);
    mbedtls_pk_init(&private_pk_context);

    mbedtls_entropy_add_source(&entropy_context,get_clock_for_entropy,NULL,MBEDTLS_ENTROPY_MIN_PLATFORM,MBEDTLS_ENTROPY_SOURCE_STRONG);
    int result = mbedtls_ctr_drbg_seed(&ctr_drbg_context, mbedtls_entropy_func, &entropy_context, personalization,strlen(personalization));
    if(result){
        printf("failed to get drbg seed\n");
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -1;
    }

    //对文件进行签名
    //从文件读取私钥
    result = get_private_key_from_file(&private_pk_context,private_pem_file);
    if(result != 0){
        printf("failed to get private key!\n");
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -2;
    }
    //计算待加密文件HASH
    result = get_hash_from_file_by_sha256(sign_verify_file,hash_buffer);
    if(result != 0){
        printf("failed to get hash from file\n");
    }
    //因PK默认填充方式为V15,若需要使用V21填充方式,需添加如下代码。若使用V15,则无需添加。若使用V21填充方式,需同步在config.h中启用#define MBEDTLS_PKCS1_V21
    //mbedtls_rsa_set_padding(mbedtls_pk_rsa(private_pk_context),MBEDTLS_RSA_PKCS_V21,MBEDTLS_MD_SHA256);
    size_t signature_length;
    result = mbedtls_pk_sign(&private_pk_context,MBEDTLS_MD_SHA256,hash_buffer,sizeof(hash_buffer),signature_info,&signature_length,mbedtls_ctr_drbg_random,&ctr_drbg_context);
    if(result != 0){
        printf("failed to signature:");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -3;
    }else{
        printf("succeeded to signature!\n");
        printf("signature info:\n");
        for(int i=0;i<signature_length;i++){
            printf("%02x",signature_info[i]);
        }
        printf("\n");
        //签名写入文件
        FILE *sign_file = fopen("D:\\tmp\\crypto\\rsa\\mbedtls_sign-pk.bin","wb");
        int write_num = fwrite(signature_info,sizeof(char),signature_length,sign_file);
        if(write_num != signature_length){
            printf("failed to write signature to file\n");
        }
        fclose(sign_file);
    }

    //修改签名信息,查看验签是否失败
    //memset(signature_info+255,0xFF,1);

    //验签
    //从文件中读取公钥
    result = get_public_key_from_file(&public_pk_context,public_pem_file);
    if(result != 0){
        printf("failed to get public key!\n");
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -4;
    }
    //计算待加密文件HASH
    result = get_hash_from_file_by_sha256(sign_verify_file,hash_buffer);
    if(result != 0){
        printf("failed to get hash from file\n");
    }
    //因PK默认填充方式为V15,若需要使用V21填充方式,需添加如下代码。若使用V15,则无需添加。若使用V21填充方式,需同步在config.h中启用#define MBEDTLS_PKCS1_V21
    //mbedtls_rsa_set_padding(mbedtls_pk_rsa(private_pk_context),MBEDTLS_RSA_PKCS_V21,MBEDTLS_MD_SHA256);
    //最后一个参数为签名长度,RSA-2048为256Bytes(先HASH在签名)
    result = mbedtls_pk_verify(&public_pk_context,MBEDTLS_MD_SHA256,hash_buffer,sizeof(hash_buffer),signature_info,256);
    if(result == 0){
        printf("succeeded to verify signature!\n");
    }else{
        printf("failed to verify signature:");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
        return -5;
    }
    free_context(&public_pk_context,&private_pk_context,&entropy_context,&ctr_drbg_context);
    return 0;

}

4.7.2.1 运行效果

1.正常运行效果
在这里插入图片描述

2.篡改签名信息后验证
代码中启用注释掉的代码memset(signature_info+255,0xFF,1);
最终提示验签失败
在这里插入图片描述

3.使用openssl验签
上述代码中,成功后的签名信息会保存到D:\tmp\crypto\rsa\mbedtls_sign-pk.bin
使用openssl命令进行验签,也可以验签通过(openssl使用PKCS#V1.5填充进行验签。若使用PKCS#V2.1签名,因填充方式不一致,会导致验签失败)
在这里插入图片描述
在这里插入图片描述

mbedtls 3.6.3 版本中进行 RSA PSS 操作时,并不需要移植 PSA 文件夹[^1]。该版本的 RSA PSS 证功能主要依赖于 mbedtls 自身的 RSA 和哈希模块,而不是基于 PSA(Platform Security Architecture)的接口[^2]。 RSA PSS 的实现位于 mbedtls 的核心库中,具体依赖的文件包括 `library/rsa.c`、`library/md.c` 以及相关的哈希算法实现(如 `library/sha256.c`)等。此外,过程需要使用哈希接口和 RSA 上下文管理,涉及的头文件包括 `mbedtls/rsa.h` 和 `mbedtls/md.h`。这些功能均不依赖 PSA 模块,因此在移植或使用过程中无需引入 `PSA` 文件夹[^3]。 如果用户的应用场景仅涉及 RSA PSS (而非使用 PSA 密码 API 或与 TrustZone 相关的安全服务),则可以安全地忽略 PSA 相关组件。此外,确保 mbedtls 的配置文件 `mbedtls/config.h` 中启用了必要的选项,例如: ```c #define MBEDTLS_RSA_C #define MBEDTLS_PKCS1_V21 #define MBEDTLS_SHA256_C ``` 这些配置项确保 RSA、PSS 填充机制和 SHA-256 哈希算法被正确启用[^4]。 ### 示例代码 以下是一个使用 mbedtls 3.6.3 进行 RSA PSS 的代码示例: ```c #include "mbedtls/rsa.h" #include "mbedtls/md.h" int verify_rsa_pss_signature(mbedtls_rsa_context *rsa_ctx, const unsigned char *hash, size_t hash_len, const unsigned char *sig) { int ret = mbedtls_rsa_rsassa_pss_verify(rsa_ctx, MBEDTLS_MD_SHA256, hash_len, hash, sig); return ret; } ``` 上述代码展示了如何调用 `mbedtls_rsa_rsassa_pss_verify` 函数进行操作,且不涉及 PSA 相关模块[^5]。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值