OpenSSL ECC 常用结构体+实例

以下内容是根据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;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值