提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
提示:这里可以添加本文要记录的大概内容:
ECDSA(Elliptic Curve Digital Signature Algorithm)是一种基于椭圆曲线密码学的数字签名算法。它利用椭圆曲线上的点构成的阿贝尔群的性质,以及椭圆曲线离散对数问题的计算难度来确保签名的安全性。ECDSA的密钥和签名相比RSA等传统算法更短,但提供相同甚至更高的安全性。例如,256位的ECDSA签名具有与3072位的RSA签名相当的安全强度 。
本章重点在ECSDA算法的签名和验签的代码实现,不做算法的详细描述
提示:以下是本篇文章正文内容,下面案例可供参考
一、ECDSA的签名验证过程?
ECDSA的密钥生成包括私钥和公钥两部分。私钥是一个在一定范围内随机选择的整数,而公钥则是通过将私钥与椭圆曲线上的一个预定义的基点相乘得到的点 。
签名过程包括以下几个步骤:
- 对消息进行哈希处理,得到哈希值。
- 选择一个安全的随机数,计算其与基点的乘积,得到一个随机点。
- 使用随机数、消息哈希值和私钥计算签名的第二部分。
- 将随机点的x坐标和计算出的第二部分作为签名输出 。
验签需要以下数据:
- 对消息进行哈希处理,得到哈希值。
- 获取发送端提供的密钥对(X, Y)。
- 获取发送端提供的签名值[R, S]。
基本概念:
- 签名:发送者通过密钥生成公钥,并使用公钥对数据生成签名值
- 验签:接收者使用发送者提供的公钥,签名值对数据进行验证
一个基本的,常被忽略的事实是,签名和验签,处理的可以是原文而不是密文。也就是说,标准的签名和验签过程中,发送方和接受方都看到的是信息明文!
二、程序实现步骤 (需引用Mbedtls库实现)
1. 签名过程
代码如下:
//提供签名数据,生成公钥对(X,Y), 生成签名[R, S]
void EcdSa_Sign_Test(uint8_t* hash, size_t hlen,
uint8_t* key_x, uint8_t* key_y,
uint8_t* sign_r, uint8_t* sign_s)
{
int ret = 1;
mbedtls_ecdsa_context ctx_sign;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
mbedtls_mpi r, s;
unsigned char public_key_buffer[65];
size_t public_key_size;
size_t rlen, slen;
mbedtls_ecdsa_init(&ctx_sign);
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
/*
* Generate a key pair for signing
*/
mbedtls_printf("\n . Seeding the random number generator...");
fflush(stdout);
mbedtls_entropy_init(&entropy);
if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
NULL, 0)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ctr_drbg_seed returned %d\n", ret);
goto exit;
}
mbedtls_printf(" ok\n . Generating key pair...");
fflush(stdout);
if ((ret = mbedtls_ecdsa_genkey(&ctx_sign, MBEDTLS_ECP_DP_SECP256R1,
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ecdsa_genkey returned %d\n", ret);
goto exit;
}
mbedtls_printf(" ok (key size: %d bits)\n", (int)ctx_sign.grp.pbits);
dump_pubkey(" + Public key: ", &ctx_sign);
dunp_Getpubkey(&ctx_sign, public_key_buffer, &public_key_size);
split_ecdsa_key(public_key_buffer, key_x, key_y);
/*
* Sign message hash
*/
mbedtls_printf(" . Signing message hash...");
fflush(stdout);
if ((ret = mbedtls_ecdsa_sign(&ctx_sign.grp, &r, &s, &ctx_sign.d,
hash, hlen,
mbedtls_ctr_drbg_random, &ctr_drbg)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ecdsa_sign returned %d\n", ret);
goto exit;
}
rlen = mbedtls_mpi_size(&r);
slen = mbedtls_mpi_size(&s);
mbedtls_mpi_write_binary(&r, sign_r, rlen);
mbedtls_mpi_write_binary(&s, sign_s, slen);
mbedtls_printf(" ok \n");
exit:
mbedtls_ecdsa_free(&ctx_sign);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
}
2. 公钥拆分和合并
代码如下:
//将公钥对(X , Y),合并成65字节的公钥
void convert_ecdsa_key(uint8_t* public_key, uint8_t* key_x, uint8_t* key_y) {
public_key[0] = 0x04; // Prepend 0x04 to indicate compressed format
memcpy(public_key + 1, key_x, 32);
memcpy(public_key + 33, key_y, 32);
}
//将65字节的公钥,拆分为公钥对(X, Y)
void split_ecdsa_key(uint8_t* public_key, uint8_t* key_x, uint8_t* key_y) {
// 复制前32字节到X
memcpy(key_x, public_key + 1, 32);
// 复制剩余字节到Y
memcpy(key_y, public_key + 33, 32);
}
3. 验签过程
代码如下:
//提供Hash数据,公钥对(X, Y), 签名值[R, S]
void Ecdsa_SignVerify_Test(uint8_t* hash, size_t hlen,
uint8_t* key_x, uint8_t* key_y,
uint8_t* sign_r, uint8_t* sign_s)
{
int ret = 1;
mbedtls_ecdsa_context ctx_verify;
mbedtls_mpi r, s;
size_t public_key_size;
unsigned char public_key_buffer[65]; // 公钥的最大大小,65字节(包括结束字节)
mbedtls_mpi_init(&r);
mbedtls_mpi_init(&s);
convert_ecdsa_key(public_key_buffer, key_x, key_y);
mbedtls_mpi_read_binary(&r, sign_r, 32);
mbedtls_mpi_read_binary(&s, sign_s, 32);
/*
* Transfer public information to verifying context
*
* We could use the same context for verification and signatures, but we
* chose to use a new one in order to make it clear that the verifying
* context only needs the public key (Q), and not the private key (d).
*/
mbedtls_printf(" . Preparing verification context...");
fflush(stdout);
mbedtls_ecdsa_init(&ctx_verify);
mbedtls_ecp_group_load(&ctx_verify.grp, MBEDTLS_ECP_DP_SECP256R1);
public_key_size = sizeof(public_key_buffer);
// 给ECDSA上下文写入公钥
if ((ret = mbedtls_ecp_point_read_binary(&ctx_verify.grp, &ctx_verify.Q, public_key_buffer, public_key_size)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ecp_point_read_binary returned %d\n", ret);
goto exit;
}
dump_pubkey(" \n + Verifying Public key: ", &ctx_verify);
/*
* Verify signature
*/
mbedtls_printf(" ok\n . Verifying signature...");
fflush(stdout);
if ((ret = mbedtls_ecdsa_verify(&ctx_verify.grp,
hash, hlen, &ctx_verify.Q, &r, &s)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_ecdsa_verify returned %d\n", ret);
goto exit;
}
mbedtls_printf(" ok\n");
exit:
#if defined(_WIN32)
mbedtls_printf(" + Press Enter to exit this program.\n");
fflush(stdout); getchar();
#endif
mbedtls_ecdsa_free(&ctx_verify);
}
4. 主函数实现
代码如下:
#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 */
#if defined(MBEDTLS_ECDSA_C) && \
defined(MBEDTLS_ENTROPY_C) && defined(MBEDTLS_CTR_DRBG_C)
#include "mbedtls/entropy.h"
#include "mbedtls/ctr_drbg.h"
#include "mbedtls/ecdsa.h"
#include "mbedtls/sha256.h"
#include <string.h>
#endif
int main( int argc, char *argv[] )
{
int ret = 0;
unsigned char message[100] = "qwertyuiopasdfghjklzxcvbnmasdqwdcsdkcasnjkc acmkscs,c";
unsigned char hash[32];
unsigned char key_X[32], key_Y[32];
unsigned char sign_R[32], sign_S[32];
size_t hlen;
/*
* Compute message hash
*/
mbedtls_printf(" . Computing message hash...");
fflush(stdout);
if ((ret = mbedtls_sha256_ret(message, sizeof(message), hash, 0)) != 0)
{
mbedtls_printf(" failed\n ! mbedtls_sha256_ret returned %d\n", ret);
}
hlen = sizeof(hash);
mbedtls_printf(" ok\n");
dump_buf(" + Hash: ", hash, hlen);
mbedtls_printf(" . Sign_Test Start ...\n");
EcdSa_Sign_Test(hash, hlen, key_X, key_Y, sign_R, sign_S);
mbedtls_printf(" . Sign_Test end ...\n");
dump_buf("\n + hash: ", hash, 32);
dump_buf("\n + key_X: ", key_X, 32);
dump_buf("\n + key_Y: ", key_Y, 32);
dump_buf("\n + sign_R: ", sign_R, 32);
dump_buf("\n + sign_S: ", sign_S, 32);
mbedtls_printf(" \n. Sign_Verify Start ...\n");
Ecdsa_SignVerify_Test(hash, hlen, key_X, key_Y, sign_R, sign_S);
}
总结
这里采用PC端验证的,也可直接移植到单片机中,需注意在单片机中使用验签,仅验签算法,堆大小需要4K,加上其他程序代码,堆大小至少要在6K,否则会出现验签失败,返回值为-16,该程序在单片机中测试需要花费800ms左右。