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

一、 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.csdn.net/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签名,因填充方式不一致,会导致验签失败)
在这里插入图片描述
在这里插入图片描述

  • 40
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
以下是一个基于RSA数字签名示例的C语言实现。这个示例使用OpenSSL库来实现RSA加密和解密。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <openssl/rsa.h> #include <openssl/pem.h> #define KEY_LENGTH 2048 #define PUB_EXP 3 int main() { char message[] = "this is a test message"; unsigned char digest[SHA256_DIGEST_LENGTH]; unsigned int digest_length; unsigned char *signature; unsigned int signature_length; RSA *rsa = RSA_generate_key(KEY_LENGTH, PUB_EXP, NULL, NULL); // 计算消息的摘要 SHA256((unsigned char *) message, strlen(message), digest); digest_length = sizeof(digest); // 使用私钥对摘要进行签名 signature = malloc(RSA_size(rsa)); RSA_sign(NID_sha256, digest, digest_length, signature, &signature_length, rsa); // 打印签名结果 printf("Message: %s\n", message); printf("Signature: "); for (int i = 0; i < signature_length; i++) { printf("%02x", signature[i]); } printf("\n"); // 使用公钥对签名进行验证 int result = RSA_verify(NID_sha256, digest, digest_length, signature, signature_length, rsa); if (result == 1) { printf("Signature is valid.\n"); } else { printf("Signature is invalid.\n"); } RSA_free(rsa); free(signature); return 0; } ``` 这个示例中,我们首先生成一个2048位的RSA密钥对,并使用SHA256算法计算出消息的摘要。接着,我们使用私钥对摘要进行签名,并打印出签名结果。最后,我们使用公钥对签名进行验证,如果验证通过,则说明签名是有效的。 需要注意的是,这个示例中使用了OpenSSL库来实现RSA加密和解密。在使用之前,请确保已经正确安装并链接了OpenSSL库。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值