密钥生成
椭圆曲线参数选择
使用以下命令列出所有支持的曲线:
openssl ecparam -list_curves
若支持目标曲线,就可以在openssl/include/openssl/obj_mac.h文件中找到相应的 NID
命令行生成与读取
这里椭圆曲线参数由name指定,如NIST P-256(secp256r1,在 OpenSSL 称为prime256v1)
生成秘钥
openssl ecparam -genkey -name prime256v1 -param_enc explicit -outform pem -out ec_prikey.pem
获取公钥
openssl ec -in ec_prikey.pem -pubout -out ec_pubkey.pem
代码生成
生成密钥对
1
2
3
4
5
6
|
#include "openssl/ec.h"
#include "openssl/obj_mac.h"
EC_KEY *key_pair = NULL;
int ret_error;
key_pair = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
ret_error = EC_KEY_generate_key(key_pair);
|
方法说明
EC_KEY
结构体内容如下
1
2
3
4
5
6
7
8
9
10
11
|
struct ec_key_st {
int version;
EC_GROUP *group;
EC_POINT *pub_key;
BIGNUM *priv_key;
unsigned int enc_flag;
point_conversion_form_t conv_form;
int references;
int flags;
EC_EXTRA_DATA *method_data;
} /* EC_KEY */;
|
函数方法说明
1
|
EC_KEY *EC_KEY_new_by_curve_name(int nid);
|
根据相关曲线的 nid
来构造新的 EC_KEY
1
|
int EC_KEY_generate_key(EC_KEY *key);
|
EC_KEY_generate_key
为 EC_KEY
对象生成新的公钥和私钥。在调用此函数之前, EC_KEY
必须具有与之关联的 EC_GROUP
对象。私钥是一个随机整数(0 < priv_key < order,其中order是EC_GROUP对象的顺序)。公钥是曲线上的 EC_POINT
,通过将曲线的生成器乘以私钥计算得出。
读取密钥
pem文件读取
一种方法是直接读取为EC_KEY
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <openssl/pem.h>
#include "openssl/bn.h"
const char * prikey_path="ec_prikey.pem";
const char * pubkey_path="ec_pubkey.pem";
EVP_PKEY *pubKey = NULL;
EC_KEY *ec_key;
BIO *bio_key_file = NULL;
/* pri key */
/*get key from pem file*/
bio_key_file = BIO_new_file(prikey_path, "rb");
ec_key = PEM_read_bio_ECPrivateKey(bio_key_file, NULL, NULL, NULL);
/* pub key */
bio_key_file = BIO_new_file(pubkey_path, "rb");
ec_key = PEM_read_bio_EC_PUBKEY(bio_key_file, NULL, NULL, NULL);
|
另一种方法是先读取为EVP_PKEY再转换为EC_KEY
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <openssl/pem.h>
#include "openssl/bn.h"
const char * prikey_path="ec_prikey.pem";
const char * pubkey_path="ec_pubkey.pem";
EVP_PKEY * pubKey = NULL;
EC_KEY * ec_key;
BIO *bio_key_file = NULL;
/* get key from pem file */
bio_key_file = BIO_new_file(pubkey_path, "rb");
/* get pub key */
pubKey = PEM_read_bio_PUBKEY(bio_key_file, NULL, NULL, NULL);
ec_key = EVP_PKEY_get1_EC_KEY(pubKey);
|
字符串读取
仅BIO读取方式不同
1
2
3
4
5
6
7
8
9
|
char *str_in_pem = "str";
BIO *bio_key = NULL;
EC_KEY *ec_key;
bio_key = BIO_new_mem_buf(str_in_pem, strlen(str_in_pem));
/* for pri key */
ec_key = PEM_read_bio_ECPrivateKey(bio_key, NULL, NULL, NULL);
/* for pub key */
ec_key = PEM_read_bio_EC_PUBKEY(bio_key, NULL, NULL, NULL);
|
方法说明
以 PEM_read_bio_ECPrivateKey
为例,在文档OpenSSL libraries里可能找不到这个方法,但是其实是能用的。获取EC_KEY有两种方式,要么接收返回值,要么在第二个参数传入key指针地址(若该指针已存在EC_KEY,则更新为现在读取到的EC_KEY,否则不做处理)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
EC_KEY *PEM_read_bio_ECPrivateKey(BIO *bp, EC_KEY **key, pem_password_cb *cb,
void *u)
{
EVP_PKEY *pktmp;
pktmp = PEM_read_bio_PrivateKey(bp, NULL, cb, u);
return pkey_get_eckey(pktmp, key); /* will free pktmp */
}
static EC_KEY *pkey_get_eckey(EVP_PKEY *key, EC_KEY **eckey)
{
EC_KEY *dtmp;
if (!key)
return NULL;
dtmp = EVP_PKEY_get1_EC_KEY(key);
EVP_PKEY_free(key);
if (!dtmp)
return NULL;
if (eckey) {
EC_KEY_free(*eckey);
*eckey = dtmp;
}
return dtmp;
}
|
消息签名
签名方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include "openssl/ecdsa.h"
#include "openssl/sha.h"
const char *message = "message to sign";
unsigned char buffer_digest[SHA256_DIGEST_LENGTH];
uint8_t *digest;
uint8_t *signature;
uint32_t signature_len;
int ret_error;
signature_len = ECDSA_size(key_pair);
signature = (uint8_t *) OPENSSL_malloc(signature_len);
digest = SHA256((const unsigned char *)message, strlen(message), buffer_digest);
ret_error = ECDSA_sign(0, (const uint8_t *)digest, SHA256_DIGEST_LENGTH, signature, &signature_len, key_pair);
|
方法说明
1
2
|
int ECDSA_sign(int type, const unsigned char *dgst, int dgstlen,
unsigned char *sig, unsigned int *siglen, EC_KEY *eckey);
|
ECDSA_sign()
使用私有 EC 密钥 eckey
计算 dgstlen
字节哈希值 dgst
的数字签名。DER 编码的签名存储在 sig
中,其长度在 siglen
中返回。注意:sig
必须指向 ECDSA_size(eckey)
字节的内存。
return: 成功时返回 1,错误时返回 0
这里siglen传入的是指针,在签名后长度可能会比传入之前的siglen短。
消息验签
验签方法
变量来自前面
1
2
|
int verification;
verification = ECDSA_verify(0, digest, SHA256_DIGEST_LENGTH, signature, signature_len, key_pair);
|
方法说明
1
2
|
int ECDSA_verify(int type, const unsigned char *dgst, int dgstlen,
const unsigned char *sig, int siglen, EC_KEY *eckey);
|
ECDSA_verify()
使用公钥 eckey
验证长为 siglen
的签名 sig
是长为dgstlen
的哈希值 dgst
的有效 ECDSA 签名。
return: 有效签名返回 1,对无效签名返回 0,对错误返回 -1
这里siglen传入的不是指针