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 表示成功。
int ECDSA_sign(int type, const unsigned char *dgst, int dlen, unsigned char
*sig, unsigned int *siglen, EC_KEY *eckey)
{
return ECDSA_sign_ex(type, dgst, dlen, sig, siglen, NULL, NULL, eckey);
}
int ECDSA_sign_ex(int type, const unsigned char *dgst, int dlen,
unsigned char *sig, unsigned int *siglen, const BIGNUM *kinv,
const BIGNUM *r, EC_KEY *eckey)
{
if (eckey->meth->sign != NULL)
return eckey->meth->sign(type, dgst, dlen, sig, siglen, kinv, r, eckey);
ECerr(EC_F_ECDSA_SIGN_EX, EC_R_OPERATION_NOT_SUPPORTED);
return 0;
}
7) ECDSA_verify
验签,返回 1 表示合法。
/*-
* returns
* 1: correct signature
* 0: incorrect signature
* -1: error
*/
int ECDSA_verify(int type, const unsigned char *dgst, int dgst_len,
const unsigned char *sigbuf, int sig_len, EC_KEY *eckey)
{
if (eckey->meth->verify != NULL)
return eckey->meth->verify(type, dgst, dgst_len, sigbuf, sig_len,
eckey);
ECerr(EC_F_ECDSA_VERIFY, EC_R_OPERATION_NOT_SUPPORTED);
return 0;
}
8) EC_KEY_get0_public_key
获取公钥。
const EC_POINT *EC_KEY_get0_public_key(const EC_KEY *key)
{
return key->pub_key;
}
int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub_key)
{
if (key->meth->set_public != NULL
&& key->meth->set_public(key, pub_key) == 0)
return 0;
EC_POINT_free(key->pub_key);
key->pub_key = EC_POINT_dup(pub_key, key->group);
return (key->pub_key == NULL) ? 0 : 1;
}
9)EC_KEY_get0_private_key
获取私钥。
const BIGNUM *EC_KEY_get0_private_key(const EC_KEY *key)
{
return key->priv_key;
}
int EC_KEY_set_private_key(EC_KEY *key, const BIGNUM *priv_key)
{
if (key->group == NULL || key->group->meth == NULL)
return 0;
if (key->group->meth->set_private != NULL
&& key->group->meth->set_private(key, priv_key) == 0)
return 0;
if (key->meth->set_private != NULL
&& key->meth->set_private(key, priv_key) == 0)
return 0;
BN_clear_free(key->priv_key);
key->priv_key = BN_dup(priv_key);
return (key->priv_key == NULL) ? 0 : 1;
}
int ECDH_compute_key(void *out, size_t outlen, const EC_POINT *pub_key,
const EC_KEY *eckey,
void *(*KDF) (const void *in, size_t inlen, void *out,
size_t *outlen))
{
unsigned char *sec = NULL;
size_t seclen;
if (eckey->meth->compute_key == NULL) {
ECerr(EC_F_ECDH_COMPUTE_KEY, EC_R_OPERATION_NOT_SUPPORTED);
return 0;
}
if (outlen > INT_MAX) {
ECerr(EC_F_ECDH_COMPUTE_KEY, EC_R_INVALID_OUTPUT_LENGTH);
return 0;
}
if (!eckey->meth->compute_key(&sec, &seclen, pub_key, eckey))
return 0;
if (KDF != NULL) {
KDF(sec, seclen, out, &outlen);
} else {
if (outlen > seclen)
outlen = seclen;
memcpy(out, sec, outlen);
}
OPENSSL_clear_free(sec, seclen);
return outlen;
}
#include
#include
#include
#include
#include
#include
#include
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;
}