mbedtls移植之ECDSA签名验签算法

本文介绍了MbedTLS在嵌入式系统中的应用,包括ECDSA算法的使用,私钥和公钥的生成、存储,以及如何通过MbedTLS进行文件签名和验签的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、 mbedtls简介

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

二、ECDSA算法简介

椭圆曲线数字签名算法(ECDSA)是使用椭圆曲线密码(ECC)对数字签名算法(DSA)的模拟。ECDSA于1999年成为ANSI标准,并于2000年成为IEEE和NIST标准。
ECDSA是ECC与DSA的结合,整个签名过程与DSA类似,所不一样的是签名中采取的算法为ECC。ECC和DSA的详细信息,此处不做介绍。

三、签名验签简介

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

四、实现

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_ECP_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#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_ASN1_WRITE_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_FS_IO

//写入证书
#define MBEDTLS_PK_WRITE_C
#define MBEDTLS_PEM_WRITE_C

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

#endif

4.2 引入头文件

引入相应的头文件

#include <stdio.h>
#include <time.h>
#include <string.h>
#include "../crypto/mbedtls/ecdsa.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 添加熵源

//添加熵源
int get_clock_for_entropy( void *data,unsigned char *output, size_t len, size_t *olen ) {
    time_t now_time;
    time(&now_time);
    unsigned long timer = now_time;
    ((void) data);
    *olen = 0;

    if (len < sizeof(unsigned long))
        return (0);

    memcpy(output, &timer, sizeof(unsigned long));
    *olen = sizeof(unsigned long);
    return 0;
}

4.4 生成公私钥对

4.4.1 生成公私钥对

//生成公私钥
int generate_ecp_keypair(){

    //个性化初始值:用于初始化伪随机数生成器,可设置为任意值
    const char *personalization = "Fr789jj-ikrkjfjs@";
    char error[100];
    mbedtls_pk_context pk_context;
    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_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 !=0 ){
        printf("failed to set ctr_drbg seed\n");
        mbedtls_entropy_free(&entropy_context);
        mbedtls_ctr_drbg_free(&ctr_drbg_context);
        return -1;
    }

    mbedtls_pk_init(&pk_context);
    mbedtls_pk_setup(&pk_context,mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
    result = mbedtls_ecp_gen_key(MBEDTLS_ECP_DP_SECP256K1,mbedtls_pk_ec(pk_context),mbedtls_ctr_drbg_random,&ctr_drbg_context);
    if(result == 0){
        printf("succeeded to generate ECC key pair\n");
    }else{
        printf("failed to generate ECC key pair\n");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        mbedtls_pk_free(&pk_context);
        mbedtls_entropy_free(&entropy_context);
        mbedtls_ctr_drbg_free(&ctr_drbg_context);
        return -2;
    }

    write_pem_key_to_file(&pk_context);

    mbedtls_pk_free(&pk_context);
    mbedtls_entropy_free(&entropy_context);
    mbedtls_ctr_drbg_free(&ctr_drbg_context);
    return 0;

}

4.4.2 公私钥写入文件

//将ecp公私钥写入到pem文件中
int write_pem_key_to_file(mbedtls_pk_context *pk_context){
    unsigned char private_key_buff[2048];
    unsigned char public_key_buff[2048];
    //unsigned char *private_point = private_key_buff;
    //unsigned char *public_point = public_key_buff;
    char error[100];

    //私钥
    int result = mbedtls_pk_write_key_pem(pk_context,private_key_buff,sizeof(private_key_buff));
    if(result != 0){
        printf("failed to write ECP private key\n");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        return -1;
    }
    size_t private_len = strlen((char *)private_key_buff);

    FILE *private_key_file = fopen("D:\\tmp\\crypto\\ecdsa\\private.pem","wb");
    if(private_key_file == NULL){
        printf("failed to open private pem file\n");
        return -2;
    }
    result = fwrite(private_key_buff,1,private_len,private_key_file);
    if(result != private_len){
        printf("failed to write private pem file\n");
        fclose(private_key_file);
        return -3;
    }
    fclose(private_key_file);

    //公钥
    result = mbedtls_pk_write_pubkey_pem(pk_context,public_key_buff,sizeof(public_key_buff));
    if(result != 0){
        printf("failed to write ECP public key\n");
        mbedtls_strerror(result,error,sizeof(error));
        printf("%s\n",error);
        return -4;
    }
    size_t public_len = strlen((char *)public_key_buff);

    FILE *public_key_file = fopen("D:\\tmp\\crypto\\ecdsa\\public.pem","wb");
    if(public_key_file == NULL){
        printf("failed to open public pem file\n");
        return -5;
    }
    result = fwrite(public_key_buff,1,public_len,public_key_file);
    if(result != public_len){
        printf("failed to write public pem file\n");
        fclose(public_key_file);
        return -6;
    }
    fclose(public_key_file);

    return 0;

}

4.4.3 从文件读取公私钥

//从文件读取私钥
int get_private_key_from_pem_file(mbedtls_pk_context *pk_context,const char *private_file_path){
    unsigned char error[100];
    int result = mbedtls_pk_parse_keyfile(pk_context,private_file_path,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;
}

//从文件读取公钥
int get_public_key_from_pem_file(mbedtls_pk_context *pk_context,const char *public_file_path){
    unsigned char error[100];
    int result = mbedtls_pk_parse_public_keyfile(pk_context,public_file_path);
    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;
}

4.5 签名和验签

4.5.1 计算HASH

当对文件进行签名时,首先使用HASH算法计算出当前文件的HASH值后,在对HASH进行签名,得到签名值

//计算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];
    int 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.5.2 签名

通过调用mbedtls_pk_sign实现签名,签名值保存到文件中。

int sign_by_ecdsa(){

    int result;
    const char *personalization = "jedifa8834JSOG&&";
    const char *private_file = "D:\\tmp\\crypto\\ecdsa\\private.pem";
    const char *signature_file = "D:\\tmp\\crypto\\ecdsa\\music.mp3";
    mbedtls_pk_context pk_context;
    unsigned char error[100];
    unsigned char hash_buffer[32];
    unsigned char signature_info[256];
    mbedtls_entropy_context entropy_context;
    mbedtls_ctr_drbg_context ctr_drbg_context;

    mbedtls_pk_init(&pk_context);
    mbedtls_entropy_init(&entropy_context);
    mbedtls_ctr_drbg_init(&ctr_drbg_context);

    mbedtls_entropy_add_source(&entropy_context,get_clock_for_entropy,NULL,MBEDTLS_ENTROPY_MIN_PLATFORM,MBEDTLS_ENTROPY_SOURCE_STRONG);
    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(&pk_context,&entropy_context,&ctr_drbg_context);
        return -1;
    }

    //加载私钥
    result = get_private_key_from_pem_file(&pk_context,private_file);
    if(result != 0){
        free_context(&pk_context,&entropy_context,&ctr_drbg_context);
        return -1;
    }

    //计算待加密文件HASH
    result = get_hash_from_file_by_sha256(signature_file,hash_buffer);
    if(result != 0){
        printf("failed to get hash from file\n");
    }
    size_t signature_length;
    result = mbedtls_pk_sign(&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(&pk_context,&entropy_context,&ctr_drbg_context);
        return -3;
    }else{
        printf("succeeded to signature!\n");
        //printf("signature length is: %zu\n",signature_length);
        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\\ecdsa\\mbedtls_sign_ecdsa.bin","wb");
        size_t 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);
    }

    free_context(&pk_context,&entropy_context,&ctr_drbg_context);

}

4.5.3 验签

从文件中读取签名值,并通过调用mbedtls_pk_verify验签。其中mbedtls_pk_verify的最后一个参数sig_len需为签名值的真实长度。

int verify_by_ecdsa(){

    int result;
    const char *public_file = "D:\\tmp\\crypto\\ecdsa\\public.pem";
    const char *signature_file = "D:\\tmp\\crypto\\ecdsa\\music.mp3";
    mbedtls_pk_context pk_context;
    unsigned char error[100];
    unsigned char hash_buffer[32];
    unsigned char signature_info[256];

    mbedtls_pk_init(&pk_context);

    //加载公钥
    result = get_public_key_from_pem_file(&pk_context,public_file);
    if(result != 0){
        mbedtls_pk_free(&pk_context);
        return -1;
    }

    //计算待加密文件HASH
    result = get_hash_from_file_by_sha256(signature_file,hash_buffer);
    if(result != 0){
        printf("failed to get hash from file\n");
    }
    //读取签名
    FILE *sign_file = fopen("D:\\tmp\\crypto\\ecdsa\\mbedtls_sign_ecdsa.bin","rb");
    //secp256k1签名长度为70-72字节
    size_t read_num = fread(signature_info,sizeof(char),256,sign_file);
    if(!feof(sign_file) || ferror(sign_file)){
        //未到达文件尾部或出错
        printf("failed to get signature file\n");
        mbedtls_pk_free(&pk_context);
        fclose(sign_file);
        return -2;
    }
    fclose(sign_file);

    result = mbedtls_pk_verify(&pk_context,MBEDTLS_MD_SHA256,hash_buffer,sizeof(hash_buffer),signature_info,read_num);
    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);
        mbedtls_pk_free(&pk_context);
        return -3;
    }

}

4.5.4 运行效果

1.成功运行
在这里插入图片描述
在这里插入图片描述

2.使用openssl验签
openssl dgst -sha256 -verify D:\tmp\crypto\ecdsa\public.pem -signature D:\tmp\crypto\ecdsa\mbedtls_sign_ecdsa.bin D:\tmp\crypto\ecdsa\music.mp3
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值