以下内容是根据openssl1.1.0+版本实现
OpenSSL实现的ECC 算法,包括三部分: ECC 算法(crypto/ec)、椭圆曲线数字签名算法 ECDSA (crypto/ecdsa)以及椭圆曲线密钥交换算法 ECDH(crypto/dh)。
密钥数据结构
密钥数据结构定义在openssl-1.1.0c\crypto\ec\ec_lcl.h文件中。
struct ec_key_st {
const EC_KEY_METHOD *meth;
ENGINE *engine;
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;
CRYPTO_EX_DATA ex_data;
CRYPTO_RWLOCK *lock;
};
密钥生成
椭圆曲线的密钥生成实现在 crytpo/ec/ec_key.c 中。 Openssl 中,椭圆曲线密钥生成时,首先用户需要选取一种椭圆曲线(openssl 的 crypto/ec/ec_curve.c 中内置实现了 67 种,调用 EC_get_builtin_curves 获取该列表),然后根据选择的椭圆曲线计算密钥生成参数 group,最后根据密钥参数 group 来生公私钥。
签名值数据结构
与 DSA 签名值一样, ECDSA 的签名结果表示为两项。 ECDSA 的签名结果数据结构定义在 crypto\ec\ec_lcl.h 中。
struct ECDSA_SIG_st {
BIGNUM *r;
BIGNUM *s;
};
签名与验签
crypto/ec/ ecdsa_sign.c 实现了签名算法,
crypto/ec/ ecdsa_vrf.c 实现了验签
密钥交换
crypto/ec/ec dh_ossl.c 实现了密钥交换算法。
主要函数
1) EC_get_builtin_curves
获取椭圆曲线列表。
size_t EC_get_builtin_curves(EC_builtin_curve *r, size_t nitems)
{
size_t i, min;
if (r == NULL || nitems == 0)
return curve_list_length;
min = nitems < curve_list_length ? nitems : curve_list_length;
for (i = 0; i < min; i++) {
r[i].nid = curve_list[i].nid;
r[i].comment = curve_list[i].comment;
}
return curve_list_length;
}
2) EC_GROUP_new_by_curve_name
根据指定的椭圆曲线来生成密钥参数。
EC_GROUP *EC_GROUP_new_by_curve_name(int nid)
{
size_t i;
EC_GROUP *ret = NULL;
if (nid <= 0)
return NULL;
for (i = 0; i < curve_list_length; i++)
if (curve_list[i].nid == nid) {
ret = ec_group_new_from_data(curve_list[i]);
break;
}
if (ret == NULL) {
ECerr(EC_F_EC_GROUP_NEW_BY_CURVE_NAME, EC_R_UNKNOWN_GROUP);
return NULL;
}
EC_GROUP_set_curve_name(ret, nid);
return ret;
}
3) int EC_KEY_generate_key
根据密钥参数生成 ECC 公私钥。
int EC_KEY_generate_key(EC_KEY *eckey)
{
if (eckey == NULL || eckey->group == NULL) {
ECerr(EC_F_EC_KEY_GENERATE_KEY, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (eckey->meth->keygen != NULL)
return eckey->meth->keygen(eckey);
ECerr(EC_F_EC_KEY_GENERATE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
return 0;
}
4) int EC_KEY_check_key
检查 ECC 密钥。
int EC_KEY_check_key(const EC_KEY *eckey)
{
if (eckey == NULL || eckey->group == NULL || eckey->pub_key == NULL) {
ECerr(EC_F_EC_KEY_CHECK_KEY, ERR_R_PASSED_NULL_PARAMETER);
return 0;
}
if (eckey->group->meth->keycheck == NULL) {
ECerr(EC_F_EC_KEY_CHECK_KEY, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
return 0;
}
return eckey->group->meth->keycheck(eckey);
}
5) int ECDSA_size
获取 ECC 密钥大小字节数。
int ECDSA_size(const EC_KEY *r)
{
int ret, i;
ASN1_INTEGER bs;
unsigned char buf[4];
const EC_GROUP *group;
if (r == NULL)
return 0;
group = EC_KEY_get0_group(r);
if (group == NULL)
return 0;
i = EC_GROUP_order_bits(group);
if (i == 0)
return 0;
bs.length = (i + 7) / 8;
bs.data = buf;
bs.type = V_ASN1_INTEGER;
/* If the top bit is set the asn1 encoding is 1 larger. */
buf[0] = 0xff;
i = i2d_ASN1_INTEGER(&bs, NULL);
i += i; /* r and s */
ret = ASN1_object_size(1, i, V_ASN1_SEQUENCE);
return (ret);
}
6) ECDSA_sign
签名,返回 1 表示成功。
7) ECDSA_verify
验签,返回 1 表示合法。
8) EC_KEY_get0_public_key
获取公钥。
9) EC_KEY_get0_private_key
获取私钥。
10) ECDH_compute_key
生成共享密钥
测试实例
实现原理:
随机生成两对儿ecdsa公私钥
使用己方私钥+对方公钥 做交叉运算 分别生成共享秘钥
#include <string.h>
#include <stdio.h>
#include <openssl/ec.h>
#include <openssl/ecdh.h>
#include <openssl/ecdsa.h>
#include <openssl/objects.h>
#include <openssl/err.h>
int main()
{
EC_KEY *key1,*key2;
const EC_POINT *pubkey1,*pubkey2;
EC_GROUP *group1,*group2;
unsigned int ret,nid,size,i,sig_len;
unsigned char *signature,digest[20];
BIO *berr;
EC_builtin_curve *curves;
int crv_len;
char shareKey1[128],shareKey2[128];
int len1,len2;
/* 构造 EC_KEY 数据结构 */
key1=EC_KEY_new();
if(key1==NULL)
{
printf("EC_KEY_new err!\n");
return -1;
}
key2=EC_KEY_new();
if(key2==NULL)
{
printf("EC_KEY_new err!\n");
return -1;
}
/* 获取实现的椭圆曲线个数 */
crv_len = EC_get_builtin_curves(NULL, 0);
curves = (EC_builtin_curve *)malloc(sizeof(EC_builtin_curve) * crv_len);
/* 获取椭圆曲线列表 */
EC_get_builtin_curves(curves, crv_len);
/*
nid=curves[0].nid;会有错误,原因是密钥太短
*/
/* 选取一种椭圆曲线 */
nid=curves[25].nid;
/* 根据选择的椭圆曲线生成密钥参数 group */
group1=EC_GROUP_new_by_curve_name(nid);
if(group1==NULL)
{
printf("EC_GROUP_new_by_curve_name err!\n");
return -1;
}
group2=EC_GROUP_new_by_curve_name(nid);
if(group1==NULL)
{
printf("EC_GROUP_new_by_curve_name err!\n");
return -1;
}
/* 设置密钥参数 */
ret=EC_KEY_set_group(key1,group1);
if(ret!=1)
{
printf("EC_KEY_set_group err.\n");
return -1;
}
ret=EC_KEY_set_group(key2,group2);
if(ret!=1)
{
printf("EC_KEY_set_group err.\n");
return -1;
}
/* 生成密钥 */
ret=EC_KEY_generate_key(key1);
if(ret!=1)
{
printf("EC_KEY_generate_key err.\n");
return -1;
}
ret=EC_KEY_generate_key(key2);
if(ret!=1)
{
printf("EC_KEY_generate_key err.\n");
return -1;
}
/* 检查密钥 */
ret=EC_KEY_check_key(key1);
if(ret!=1)
{
printf("check key err.\n");
return -1;
}
/* 获取密钥大小 */
size=ECDSA_size(key1);
printf("size %d \n",size);
for(i=0;i<20;i++)
memset(&digest[i],i+1,1);
signature= (unsigned char*)malloc(size);
ERR_load_crypto_strings();
berr=BIO_new(BIO_s_file());
//BIO_set_fp(berr,stdout,BIO_NOCLOSE);
/* 签名数据,本例未做摘要,可将 digest 中的数据看作是 sha1 摘要结果 */
ret=ECDSA_sign(0,digest,20,signature,&sig_len,key1);
if(ret!=1)
{
ERR_print_errors(berr);
printf("sign err!\n");
return -1;
}
/* 验证签名 */
ret=ECDSA_verify(0,digest,20,signature,sig_len,key1);
if(ret!=1)
{
ERR_print_errors(berr);
printf("ECDSA_verify err!\n");
return -1;
}
/* 获取对方公钥,不能直接引用 */
pubkey2 = EC_KEY_get0_public_key(key2);
/* 生成一方的共享密钥 */
len1= ECDH_compute_key(shareKey1, 128, pubkey2, key1, NULL);
pubkey1 = EC_KEY_get0_public_key(key1);
/* 生成另一方共享密钥 */
len2= ECDH_compute_key(shareKey2, 128, pubkey1, key2, NULL);
if(len1!=len2)
{
printf("err\n");
}
else
{
ret=memcmp(shareKey1,shareKey2,len1);
if(ret==0)
printf("生成共享密钥成功\n");
else
printf("生成共享密钥失败\n");
}
printf("test ok!\n");
BIO_free(berr);
EC_KEY_free(key1);
EC_KEY_free(key2);
free(signature);
free(curves);
return 0;
}