基于命令行参数
- 生成 secp256k1 椭圆曲线参数
openssl ecparam -out demo.param -name secp256k1
- 生成椭圆曲线密钥
openssl genpkey -paramfile demo.param -out demo.pem
- 提取密钥
openssl ec -in demo.pem -text
- 提取私钥 pem
openssl ec -in demo.pem
- 提取公钥 pem
openssl ec -in demo.pem -pubout -out ec-pub.pem
- 对待签名数据生成摘要(data.txt 文件里的内容即签名的原文)
openssl dgst -sha256 -binary -out data.sha256 data.txt
- 基于摘要执行签名
openssl pkeyutl -sign -inkey demo.pem -in data.sha256 -out data.sig
- 基于签名数据与原文执行签名验证
openssl dgst -sha256 -verify ec-pub.pem -signature data.sig data.txt
基于 C 语言编程接口
以下 TestEcdsa 函数演示了多种密钥生成与初始化的方法,以及签名、验证签名。可对应改变注释代码块进行验证。注意,函数最后未做严格的内存清理。
#include <openssl/obj_mac.h>
#include <openssl/ecdsa.h>
void TestEcdsa() {
BIO *key_bio = NULL;
EC_KEY *eckey;
// // 生成密钥
// eckey = EC_KEY_new_by_curve_name(NID_secp192k1);
// if (!eckey) {
// }
// if (!EC_KEY_generate_key(eckey)) {
// }
// // 从文件读入密钥
// // openssl ecparam -out demo.param -name secp256k1
// // openssl genpkey -paramfile demo.param -out demo.pem
// key_bio = BIO_new(BIO_s_file());
// if (!key_bio) {
// printf("BIO_new failed!\n");
// return;
// }
// BIO_read_filename(key_bio, "demo.pem");
// eckey = PEM_read_bio_ECPrivateKey(key_bio, NULL, NULL, NULL);
// if (!eckey) {
// printf("PEM_read_bio_ECPrivateKey failed!\n");
// return;
// }
// 从 PEM 形式私钥字符串初始化
// openssl ec -in demo.pem
char *pri_key = "-----BEGIN EC PRIVATE KEY-----\n\
MHQCAQEEIKg5Yz8JR2POo8dbgxCi0ycU9PB840tuYX2duY/esnbxoAcGBSuBBAAK\n\
oUQDQgAEfkHkd/srpr+qnv5mYR1/MccSTyZXJ5ph4DJbJsL7tin8FxM8nmob/CQf\n\
uDfP+cZ3ixqjNAHL3TJGQ8axs8ydbw==\n\
-----END EC PRIVATE KEY-----";
key_bio = BIO_new_mem_buf(pri_key, strlen(pri_key));
if (!key_bio){
printf("BIO_new_mem_buf failed!\n");
return;
}
eckey = PEM_read_bio_ECPrivateKey(key_bio, NULL, NULL, NULL);
if (!eckey) {
printf("PEM_read_bio_EC_PUBKEY failed!\n");
return;
}
// 执行签名
char *digest = "Ia4TUC6Yo+H1LNLYlrEMxecXmLeD38foe593nQTb9t8=";
ECDSA_SIG *sig = ECDSA_do_sign((unsigned char*)digest, strlen(digest), eckey);
if (!sig) {
printf("ECDSA_do_sign failed!\n");
return;
}
// // 从 PEM 形式公钥字符串初始化
// // openssl ec -in demo.pem -pubout
// BIO *pub_bio = NULL;
// EC_KEY *eckey_pub;
// char *pub_key = "-----BEGIN PUBLIC KEY-----\n\
// MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEfkHkd/srpr+qnv5mYR1/MccSTyZXJ5ph\n\
// 4DJbJsL7tin8FxM8nmob/CQfuDfP+cZ3ixqjNAHL3TJGQ8axs8ydbw==\n\
// -----END PUBLIC KEY-----";
// pub_bio = BIO_new_mem_buf(pub_key, strlen(pub_key));
// if (!pub_bio){
// printf("BIO_new_mem_buf failed!\n");
// return;
// }
// eckey_pub = PEM_read_bio_EC_PUBKEY(pub_bio, NULL, NULL, NULL);
// if (!eckey_pub) {
// printf("PEM_read_bio_EC_PUBKEY failed!\n");
// return;
// }
// 从原始数据格式公钥初始化
// openssl ec -in demo.pem -text
unsigned char raw[65] = {0x04, 0x7e, 0x41, 0xe4, 0x77, 0xfb, 0x2b, 0xa6, 0xbf, 0xaa, 0x9e, 0xfe, 0x66, 0x61, 0x1d,
0x7f, 0x31, 0xc7, 0x12, 0x4f, 0x26, 0x57, 0x27, 0x9a, 0x61, 0xe0, 0x32, 0x5b, 0x26, 0xc2,
0xfb, 0xb6, 0x29, 0xfc, 0x17, 0x13, 0x3c, 0x9e, 0x6a, 0x1b, 0xfc, 0x24, 0x1f, 0xb8, 0x37,
0xcf, 0xf9, 0xc6, 0x77, 0x8b, 0x1a, 0xa3, 0x34, 0x01, 0xcb, 0xdd, 0x32, 0x46, 0x43, 0xc6,
0xb1, 0xb3, 0xcc, 0x9d, 0x6f};
EC_KEY *eckey_pub = EC_KEY_new_by_curve_name(NID_secp256k1);
const unsigned char *ptr = raw;
o2i_ECPublicKey(&eckey_pub, &ptr, sizeof(raw));
// 验证签名
int ret = ECDSA_do_verify((unsigned char*)digest, strlen(digest), sig, eckey_pub);
// int ret = ECDSA_do_verify((unsigned char*)digest, strlen(digest), sig, eckey);
if (ret == -1) {
printf("verify failed!\n");
}
else if (ret == 0) {
printf("incorrect signature!\n");
}
else { /* ret == 1 */
printf("signature ok\n");
}
//TODO clean up
}
参考
- https://www.jianshu.com/p/bcad51c9a200
- https://zhuanlan.zhihu.com/p/517378832
- https://blog.csdn.net/weixin_44435894/article/details/123050279
- https://www.openssl.org/docs/man1.0.2/man1/ec.html
- https://www.openssl.org/docs/man1.0.2/man3/ec.html
- https://www.openssl.org/docs/man1.0.2/man3/ecdsa.html
- https://stackoverflow.com/questions/56218946/convert-a-plain-public-key-to-pem