一、 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