1.直接上源碼:
#include <stdio.h>
#include <string.h>
#include <openssl/ecdsa.h>
#include <openssl/pem.h>
#include <openssl/err.h>
// base64 編碼
char *base64_encode(const char *buffer, int length) {
BIO *bmem = NULL;
BIO *b64 = NULL;
BUF_MEM *bptr;
char *buff = NULL;
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new(BIO_s_mem());
b64 = BIO_push(b64, bmem);
BIO_write(b64, buffer, length);
BIO_flush(b64);
BIO_get_mem_ptr(b64, &bptr);
BIO_set_close(b64, BIO_NOCLOSE);
buff = (char *)malloc(bptr->length + 1);
memcpy(buff, bptr->data, bptr->length);
buff[bptr->length] = 0;
BIO_free_all(b64);
return buff;
}
// base64 解碼
char *base64_decode(char *input, int length) {
BIO *b64 = NULL;
BIO *bmem = NULL;
char *buffer = NULL;
buffer = (char *)malloc(length);
memset(buffer, 0, length);
b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
bmem = BIO_new_mem_buf(input, length);
bmem = BIO_push(b64, bmem);
BIO_read(bmem, buffer, length);
BIO_free_all(bmem);
return buffer;
}
//公鑰驗證簽名
int my_verify(const char *input, int input_len, ECDSA_SIG *signret, const char *pub_key_fn)
{
EC_KEY *p_dsa = NULL;
FILE *file = NULL;
int ret = 0;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int digest_len = 0;
EVP_MD_CTX md_ctx;
if((file = fopen(pub_key_fn, "rb")) == NULL) {
ret = -1;
return ret;
}
if((p_dsa = PEM_read_EC_PUBKEY(file, NULL,NULL,NULL )) == NULL) { // 獲取公鑰的ec key
ret = -2;
fclose(file);
return ret;
}
fclose(file);
EVP_MD_CTX_init(&md_ctx);
if (!EVP_DigestInit(&md_ctx, EVP_sha256())) {
printf("EVP_digest fail \n");
return -1;
}
if (!EVP_DigestUpdate(&md_ctx, (const void *)input, input_len)) {
printf("EVP_DigestUpdate fail \n");
return -1;
}
if (!EVP_DigestFinal(&md_ctx, digest, &digest_len)) { // 待簽名消息用sha256生成256比特的簽名摘要
printf("EVP_DigestFinal fail \n");
return -1;
}
printf("verify digest: %s\n", digest);
ret = ECDSA_do_verify(digest, digest_len, signret, p_dsa); // 對簽名摘要進行驗簽得到結果
if (ret == -1) {
ret = -3;
printf("ECDSA_verify err!\n");
EC_KEY_free(p_dsa);
return ret;
} else if (ret == 0) {
ret = -3;
printf("ECDSA_verify err incorrect signature!\n");
EC_KEY_free(p_dsa);
return ret;
} else {
printf("ECDSA_verify ok\n");
}
printf("verify is ok!\n");
EC_KEY_free(p_dsa);
return 0;
}
//私鑰簽名
int my_sign(const char *input, int input_len, const char *pri_key_fn)
{
EC_KEY *p_dsa = NULL;
ECDSA_SIG *s;
FILE *file = NULL;
unsigned char *data[2];
int nid;
int signlen = 0;
int i = 0;
int ret = 0;
unsigned char digest[EVP_MAX_MD_SIZE];
unsigned int digest_len = 0;
EVP_MD_CTX md_ctx;
memset(data, 0x00, sizeof(data));
nid = 0;
file = fopen(pri_key_fn, "rb");
if(!file)
{
ret = -1;
return ret;
}
if((p_dsa = PEM_read_ECPrivateKey(file, NULL, NULL, NULL)) == NULL) { // 獲取私鑰的ec key
ret = -2;
fclose(file);
return ret;
}
fclose(file);
EVP_MD_CTX_init(&md_ctx);
if (!EVP_DigestInit(&md_ctx, EVP_sha256())) {
printf("EVP_digest fail \n");
return -1;
}
if (!EVP_DigestUpdate(&md_ctx, (const void *)input, input_len)) {
printf("EVP_DigestUpdate fail \n");
return -1;
}
if (!EVP_DigestFinal(&md_ctx, digest, &digest_len)) { // 待簽名消息用sha256生成256比特的簽名摘要
printf("EVP_DigestFinal fail \n");
return -1;
}
printf("sign digest: %s\n", digest);
s = ECDSA_do_sign(digest, digest_len, p_dsa); // 對簽名摘要進行簽名得到簽名數(shù)據(jù)s
if(s == NULL) {
ret = -3;
EC_KEY_free(p_dsa);
return ret;
}
data[0] = BN_bn2hex(s->r); //二進制轉(zhuǎn)十六進制
data[1] = BN_bn2hex(s->s);
EC_KEY_free(p_dsa);
ECDSA_SIG_free(s);
printf("%s\n", data[0]);
printf("%s\n", data[1]);
free(data[0]);
free(data[1]);
return 0;
}
int main(int argc, char**argv)
{
char src[512+1];
char dst_str[2][512+1];
int src_len;
int ret;
FILE *f;
memset(src, 0x00, sizeof(src));
memset(dst_str, 0x00, sizeof(dst_str));
if(argv[1][0] == 's') {
strcpy(src, "hello world"); // 待簽名消息
src_len = strlen(src) ;
ret = my_sign(src, src_len, argv[2]);
if(ret) {
fprintf(stderr, "Error\n");
}
}
else {
ECDSA_SIG *s = (ECDSA_SIG *)malloc(sizeof(ECDSA_SIG));
strcpy(src, "hello world"); // 需要驗證的簽名消息
strncpy(dst_str[0], argv[2], 512);
strncpy(dst_str[1], argv[3], 512);
src_len = strlen(src);
s->r = BN_new();
s->s = BN_new();
BN_hex2bn(&(s->r), dst_str[0]); //十六進制轉(zhuǎn)二進制
BN_hex2bn(&(s->s), dst_str[1]);
ret = my_verify(src, src_len, s, argv[1]);
if(ret) {
fprintf(stderr, "Error\n");
}
BN_free(s->r);
BN_free(s->s);
free(s);
}
return 0;
}
2.編譯環(huán)境
openssl版本為1.0.2g,openssl version查看openssl的版本,其他版本自行驗證
base的編解碼代碼也有色洞,這里demo暫不使用
3.編譯
gcc ecdsa.c -o ecdsa -lssl -lcrypto
4.生成私鑰和公鑰
openssl ecparam -genkey -name prime256v1 -out eccpri256.key
openssl ec -in eccpri256.key -pubout -out eccpri256.pem
5.運行結果
root@ubuntu:/home/workspace/test/demo_sign# ./ecdsa s eccpri256.key
sign digest: 1M'1M¥.R?}?尣zS??Ω
E948080F0496BEAF2303A184BF9F67491AB95920BD3951DBABA36813768273DF
568224ECBA9A83161E1494DAB02884B123A376DE57A9A7BA4F018A5BB9E38404
root@ubuntu:/home/workspace/test/demo_sign# ./ecdsa eccpri256.pem E948080F0496BEAF2303A184BF9F67491AB95920BD3951DBABA36813768273DF 568224ECBA9A83161E1494DAB02884B123A376DE57A9A7BA4F018A5BB9E38404
verify digest: 1M'1M¥.R?}?尣zS??Ω3*fO@
ECDSA_verify ok
verify is ok!